UNPKG

9.95 kBMarkdownView Raw
1# @commonshost/manifest πŸ“‘
2
3**HTTP/2 Server Push Manifest** is a declarative configuration syntax for web servers, CDNs, and tools that offers web developers access to this powerful new protocol feature.
4
5```js
6[
7 {
8 get: '/index.html',
9 push: '/**/*.{js,css}'
10 }
11]
12```
13
14## JSON Schema
15
16See: [./schema.json](./schema.js)
17
18## Manifest Format
19
20The idea is simple: specify a list of **trigger and action tuples**. The syntax is flexible and forgiving, with shorthand notation for ease of use.
21
22A **manifest** is a JSON array containing rules.
23
24```js
25[]
26```
27
28Each **rule** is a JSON object containing a `get` and `push` property. These are the **trigger** and **action** respectively.
29
30```js
31[
32 {get, push},
33 {get, push}, ...
34]
35```
36
37**Triggers** are *URI Template* patterns or file path *globs*. They match the response's pathname, or the resource on disk (in the case of static sites).
38
39```js
40[
41 {get: '/index.html', push},
42 {get: 'https://example.net/', push}, ...
43]
44```
45
46**Actions** are the resources to be pushed. These are also file path *globs* or *URI templates*.
47
48```js
49[
50 {get: '/index.html', push: '/app.js'},
51 {get: '/app.js', push: '/lib.js'}, ...
52]
53```
54
55**Globs**, and **extglobs**, are POSIX/Bash-compatible expressions to match file paths. They support wildcards, negations, expansions, and more. See [micromatch](https://github.com/micromatch/micromatch).
56
57```js
58[
59 {get: '/index.html',
60 push: '/{css,js}/**/*'}, ...
61]
62```
63
64**URI Templates** are strings formatted to match URLs. They support for pathname expansion, order-independent query string fields, and wildcards. See [RFC 6570](https://tools.ietf.org/html/rfc6570).
65
66```js
67[
68 {get: 'https://example.net/shop{/brand}',
69 push: 'https://example.net/banners{/brand}.png'},
70 {get: 'https://example.net{/topic}',
71 push: 'https://api.example.net/topics{/productId}.json'}, ...
72]
73```
74
75In **shorthand** notation, both actions and triggers can be expressed as JSON strings, or arrays of strings.
76
77```js
78[
79 {get: ['/*.html', '!/legacy.html'],
80 push: ['/images/*.{png,jpg}', '!/images/huge.jpg']}, ...
81]
82```
83
84In **normalised** notation, they are arrays containing objects with `uri` and/or `glob` properties, which are in turn either strings or arrays of strings.
85
86The **priority** number is a property of the normalised push action and ranges from `0` to `255`. Defaults to `16` as per the HTTP/2 spec.
87
88```js
89[
90 {
91 get: [
92 {
93 uri: [
94 'https://example.net/',
95 'https://example.net/about', ...
96 ],
97 glob: [
98 '/images/*.{jpg,webp}',
99 '/styles/**/*.css', ...
100 ]
101 }, ...
102 ],
103 push: [
104 {
105 glob: [
106 '/fonts/*.ttf', ...
107 ],
108 uri: [
109 'https://cdn.example.net/logo.svg', ...
110 ],
111 priority: 100
112 }, ...
113 ]
114 }, ...
115]
116```
117
118Take a look at the [test cases](./test) for more normalisation examples.
119
120## FAQ
121
122### What problem does this solve?
123
124One of the features introduced by HTTP/2 is Server Push. Difficulty in configuring servers has [inhibited widespread adoption](https://youtu.be/wR1gF5Lhcq0?t=48m14s) by web developers. This declarative **HTTP/2 Server Push Manifest** format offers a server- and language-agnostic abstraction.
125
126### Why declarative rules?
127
128Declarative rules are passive, i.e. they do not require arbitrary code execution. This is ideal for processing by edge services like a CDN nodes, or other static file servers.
129
130### Which software supports HTTP/2 Manifests?
131
132This is currently supported by [@commonshost/server](https://www.npmjs.com/package/@commonshost/server), a Node.js based static web server. The [commons.host](https://commons.host) static hosting & CDN service also supports it, as it is based on the same server. Build-tool support (i.e. dependency graph tracing) is currently in development.
133
134*Implementors: Please add your implementation to this paragraph by opening a pull request.*
135
136### Aren't CDNs and web servers using preload link headers?
137
138Yes, and those too could be generated based on a Server Push manifest. For example, [@commonshost/server](https://www.npmjs.com/package/@commonshost/server) automatically outputs `Link: https://...; rel=preload` headers for clients connecting via HTTP/1.1, and uses `PUSH_PROMISE` frames for HTTP/2 clients.
139
140But those `Link` headers are arguably a leftover from the HTTP/1 era. Legacy CDN, proxy, and other middlebox support are often limiting factors.
141
142In practice it is unlikely that you can send more than a handful HTTP/1 `Link` headers. It is much more efficient to use `PUSH_PROMISE` frames, which are compressed, virtually unlimited in number, and offer more functionality.
143
144### Can I still programmatically push assets?
145
146Sure, that is an implementation issue. Your backend web application could emit `PUSH_PROMISE` frames which an intermediary CDN edge combines with a push manifest.
147
148### Does this work with a CDN?
149
150CDNs are ideally suited for server push manifests. Edge nodes can make full use of the *think time* between the receipt of a browser request and the response from an upstream, origin server.
151
152### What about browser support?
153
154The manifest is not applicable to browsers. No real considerations.
155
156All current popular web browsers (Cr/Fx/Sf/Eg) support HTTP/2 Server Push. While some complications, and occasionally bugs, exist, these will be solved faster if more people start using HTTP/2 Server Push.
157
158### Which files can be pushed?
159
160Anything goes. All you need is a URL or file name.
161
162### Should this be an RFC?
163
164Probably not, since it is an implementation detail for servers and other tools. Parts of it rely on RFCs (URI Templates, HTTP/2 spec) and other "open standards" like JSON (it's complicated) or Bash extglobs (good luck referencing the exact ISO POSIX spec!).
165
166### Why JSON?
167
168JSON is widely supported by programming languages used on the web.
169
170### Why publish this as a spec?
171
172So that, hopefully, other server and tool builders avoid reinventing the wheel. Makes it easier to build tools that target the manifest format rather than specific server or CDN implementations.
173
174### Is JavaScript required?
175
176No, the spec is a language-neutral JSON Schema. The JavaScript code is simply a proof-of-concept implementation.
177
178### Can I use HTTP/2 Server Push with CORS assets?
179
180Yes, in theory. The manifest allows pushing URIs.
181
182In practice, browser support is still limited. This area is highly experimental with several related proposals currently under consideration at the IETF HTTP-WG.
183
184### Are there security considerations?
185
186Servers and CDNs coalescing multiple origins must take care that a manifest's origin is authorised to push CORS assets from other origins.
187
188### What about caching?
189
190Caching and other headers to be included in a `PUSH_PROMISE` frame are not currently part of this spec. Share your thoughts in the issue tracker if you would like to see support for this feature.
191
192### What about HTTP methods, status codes, etc?
193
194Currently only pushes in response to a `GET` request are described in the manifest. However there is nothing technically preventing a future version of the spec from supporting other methods like `POST` or `PUT`, or other (pseudo-)headers. Please share your interest in these use-cases on the issue tracker.
195
196### Will the server push too much stuff?
197
198That depends on your manifest. With great power comes great responsibility. Protip: Use negated globs (e.g. `!/**.*.map`) to exclude files.
199
200### What if the browser already has some assets in its cache?
201
202When the server pushes a stream, the browser can quickly close it to avoid wasting resources. Unfortunately this still requires a round-trip.
203
204Consider using HTTP/2 Cache Digests. This spec allows browsers to send a condensed list of its cache contents to the server. The server can use this to avoid sending redundant assets.
205
206### What about auto-push?
207
208Several attempts have been made at automatically pushing assets, by build-tools scanning for dependencies or by intellgent servers analysing traffic patterns. The result of these can be encoded as manifest rules for portability or caching.
209
210### What about stream dependencies and priorities?
211
212Dependencies can be inferred by recursively tracing the manifest rules. For example resource A has a rule that pushes resource B, which pushes resource C in another rule. Now C is a dependency of B which is a dependency of A.
213
214Priorities are explicitly specified on each rule using the `priority` key. Its value is an integer from 0 to 255 and defaults to 16. This follows the HTTP/2 spec. All pushed resources in a rule share the same priority.
215
216### I thought HTTP/2 priority was a client-only feature?
217
218Correct, oh wise one. The priority of HTTP/2 streams is a *suggestion* by the client that the server *may* follow. Ultimately, however, it is the server that controls the order of its data frames. This manifest spec introduces a way for web developers to instruct the server on the priority of streams.
219
220### I (do/don't) like this
221
222Not really a question. But feel free to share your experience, thoughts, and suggestions on the issue tracker. Open discussion and feedback is most welcome.
223
224## API
225
226```js
227const {validate, normalise, schema} = require('@commonshost/manifest')
228
229const manifest = [
230 {
231 get: '/index.html',
232 push: ['/app.js', '/design.css']
233 }
234]
235```
236
237Check if the syntax is valid.
238
239```js
240try {
241 validate(manifest)
242 // Returns `true` or throws a validation error
243} catch (error) {
244 console.error(error)
245}
246```
247
248Transform the shorthand rules into a fully exploded format.
249
250```js
251normalise(manifest)
252
253// [
254// {
255// get: [{glob: ['/index.html']}],
256// push: [{glob: ['/app.js', '/design.css'], priority: 16}]
257// }
258// ]
259```
260
261Use the JSON Schema with your validator of choice.
262
263```js
264const validator = new MyValidator()
265validator.validate(manifest, schema)
266```
267
268## Colophon
269
270Made with πŸ’˜ by Sebastiaan Deckers in πŸ‡ΈπŸ‡¬ Singapore.