• This repository has been archived on 09/Feb/2023
  • Stars
    star
    425
  • Rank 102,094 (Top 3 %)
  • Language
    TypeScript
  • License
    Other
  • Created over 7 years ago
  • Updated almost 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

⚠️Maintenance mode⚠️ An HTTP server for Node designed to serve PRPL apps in production.

Travis Build Status AppVeyor Build Status NPM version

prpl-server

An HTTP server for Node designed to serve PRPL apps in production.

⚠️IMPORTANT⚠️ prpl-server is in maintenance mode, and is no longer recommended. Reports and PRs for critical bugs and security issues will be accepted, but we will no longer accept new feature requests or PRs.

For differential serving, we now recommend a simple two-build configuration using nomodule for client-side capability sniffing. See https://jasonformat.com/modern-script-loading/ for more details on this pattern.

If you are looking for a modern development sever, we recommend @web/dev-server.

In addition, note that Chromium is considering removing support for Server Push in a future version. Consider using <link rel="preload"> tags as a simpler alternative with similar performance benefits.

Contents

Usage

As a binary

$ npm install -g prpl-server
$ prpl-server --root . --config polymer.json

As a library

$ npm install --save prpl-server
prpl = require('prpl-server');
express = require('express');

const app = express();

app.get('/api/launch', (req, res, next) => res.send('boom'));

app.get('/*', prpl.makeHandler('.', {
  builds: [
    {name: 'modern', browserCapabilities: ['es2015', 'push']},
    {name: 'fallback'},
  ],
}));

app.listen(8080);

Differential Serving

Modern browsers offer great features that improve performance, but most applications need to support older browsers too. prpl-server can serve different versions of your application to different browsers by detecting browser capabilities using the user-agent header.

Builds

prpl-server understands the notion of a build, a variant of your application optimized for a particular set of browser capabilities.

Builds are specified in a JSON configuration file. This format is compatible with polymer.json, so if you are already using polymer-cli for your build pipeline, you can annotate your existing builds with browser capabilities, and copy the configuration to your server root. prpl-server will look for a file called polymer.json in the server root, or you can specify it directly with the --config flag.

In this example we define two builds, one for modern browsers that support ES2015 and HTTP/2 Push, and a fallback build for other browsers:

{
  "entrypoint": "index.html",
  "builds": [
    {"name": "modern", "browserCapabilities": ["es2015", "push"]},
    {"name": "fallback"}
  ]
}

Capabilities

The browserCapabilities field defines the browser features required for that build. prpl-server analyzes the request user-agent header and picks the best build for which all capabilities are met. If multiple builds are compatible, the one with more capabilities is preferred. If there is a tie, the build that comes earlier in the configuration file wins.

You should always include a fallback build with no capability requirements. If you don't, prpl-server will warn at startup, and will return a 500 error on entrypoint requests to browsers for which no build can be served.

The following keywords are supported. See also the browser-capabilities library which prpl-server uses.

Keyword Description
push HTTP/2 Server Push
serviceworker Service Worker API
modules JavaScript Modules (including dynamic import() and import.meta)
es2015 ECMAScript 2015 (aka ES6)
es2016 ECMAScript 2016
es2017 ECMAScript 2017
es2018 ECMAScript 2018

Entrypoint

In the PRPL pattern, the entrypoint is a small HTML file that acts as the application bootstrap.

prpl-server will serve the entrypoint from the best compatible build from /, and from any path that does not have a file extension and is not an existing file.

prpl-server expects that each build subdirectory contains its own entrypoint file. By default it is index.html, or you can specify another name with the entrypoint configuration file setting.

Note that because the entrypoint is served from many URLs, and varies by user-agent, cache hits for the entrypoint will be minimal, so it should be kept as small as possible.

Base paths

Since prpl-server serves resources from build subdirectories, your application source can't know the absolute URLs of build-specific resources upfront.

For most documents in your application, the solution is to use relative URLs to refer to other resources in the build, and absolute URLs to refer to resources outside of the build (e.g. static assets, APIs). However, since the entrypoint is served from URLs that do not match its location in the build tree, relative URLs will not resolve correctly.

The solution we recommend is to place a <base> tag in your entrypoint to anchor its relative URLs to the correct build subdirectory, regardless of the URL the entrypoint was served from. You may then use relative URLs to refer to build-specific resources from your entrypoint, as though you were in your build subdirectory. Put <base href="/"> in your source entrypoint, so that URLs resolve when serving your source directly during development. In your build pipeline, update each entrypoint's base tag to match its build subdirectory (e.g. <base href="/modern/">).

If you are using polymer-cli, set {"autoBasePath": true} in your polymer.json to perform this base tag update automatically.

Note that <base> tags only affect relative URLs, so to refer to resources outside of the build from your entrypoint, use absolute URLs as you normally would.

HTTP/2 Server Push

Server Push allows an HTTP/2 server to preemptively send additional resources alongside a response. This can improve latency by eliminating subsequent round-trips for dependencies such as scripts, CSS, and HTML imports.

Push manifest

prpl-server looks for a file called push-manifest.json in each build subdirectory, and uses it to map incoming request paths to the additional resources that should be pushed with it. The original push manifest file format is described here. Tools for generating a push manifest include http2-push-manifest and polymer-cli.

Each key in the push manifest is a regular expression pattern that will be matched against the incoming request path. Patterns are forced to match exactly (e.g. foo.html is equivalent to ^foo.html$). You can use wildcard patterns to push resources for client-side application routes (e.g. /articles/.*). In the case of the entrypoint, the resolved filename (e.g. index.html) is used as a key to the push manifest, in addition to the application route.

Resources in the push manifest can be specified as absolute or relative paths. Absolute paths are interpreted relative to the server root directory. Relative paths are interpreted relative to the location of the push manifest file itself (i.e. the build subdirectory), so that they do not need to know which build subdirectory they are being served from. Push manifests generated by polymer-cli always use relative paths.

Link preload headers

prpl-server is designed to be used behind an HTTP/2 reverse proxy, and currently does not generate push responses itself. Instead it sets preload link headers, which are intercepted by cooperating reverse proxy servers and upgraded into push responses. Servers that implement this upgrading behavior include Apache, nghttpx, and Google App Engine.

If a build with a push manifest is served to a browser that does not support push according to the browser-capabilities support matrix, then a nopush attribute is added to the generated preload link headers.

Testing push locally

To confirm your push manifest is working during local development, you can look for Link: <URL>; rel=preload response headers in your browser dev tools.

To see genuine push locally, you will need to run a local HTTP/2 reverse proxy such as nghttpx:

  • Install nghttpx (Homebrew, Ubuntu, source).
  • Generate a self-signed TLS certificate, e.g. openssl req -newkey rsa:2048 -x509 -nodes -keyout server.key -out server.crt
  • Start prpl-server (assuming default 127.0.0.1:8080).
  • Start nghttpx: nghttpx -f127.0.0.1,8443 -b127.0.0.1,8080 server.key server.crt --no-ocsp
  • Visit https://localhost:8443. In Chrome, Push responses will show up in the Network tab as Initiator: Push / Other.

Note that Chrome will not allow a service worker to be registered over HTTPS with a self-signed certificate. You can enable chrome://flags/#allow-insecure-localhost to bypass this check. See this page for more tips on developing service workers in Chrome.

Service Workers

Scope header

prpl-server sets the Service-Worker-Allowed header to / for any request path ending with service-worker.js. This allows a service worker served from a build subdirectory to be registered with a scope outside of that directory, e.g. register('service-worker.js', {scope: '/'}).

404 handling

prpl-server automatically serves a tiny self-unregistering service worker for any request path ending with service-worker.js that would otherwise have had a 404 Not Found response. To disable this behavior, set unregisterMissingServiceWorkers: false in your configuration file.

This can be useful when the location of a service worker has changed, as it will prevent clients from getting stuck with an old service worker indefinitely.

This problem arises because when a service worker updates, a 404 is treated as a failed update. It does not cause the service worker to be unregistered. See w3c/ServiceWorker#204 for more discussion of this problem.

HTTPS

Your apps should always be served over HTTPS. It protects your user's data, and is required for features like service workers and HTTP/2.

If the --https-redirect flag is set, prpl-server will redirect all HTTP requests to HTTPS. It sends a 301 Moved Permanently redirect to an https:// address with the same hostname on the default HTTPS port (443).

prpl-server trusts X-Forwarded-Proto and X-Forwarded-Host headers from your reverse proxy to determine the client's true protocol and hostname. Most reverse proxies automatically set these headers, but if you encounter issues with redirect loops, missing or incorrect X-Forwarded-* headers may be the cause.

You should always use --https-redirect in production, unless your reverse proxy already performs HTTPS redirection.

Caching

By default, prpl-server sets the Cache-Control header to max-age=60 (1 minute), except for the entrypoint and service worker which gets max-age=0. ETag headers are also sent, so resources that have not changed on the server can be re-validated efficiently.

To change this default for non-entrypoint resources, set the cacheControl property in your configuration file, or the --cache-control command-line flag, to the desired Cache-Control header value. You may want to set --cache-control=no-cache during development.

For more advanced caching behavior, use prpl-server as a library with Express and register a middleware that sets the Cache-Control header before registering the prpl-server middleware. If prpl-server sees that the Cache-Control header has already been set, it will not modify it. For example, to set year-long caching for images:

app.get('/images/*', (req, res, next) => {
  res.setHeader('Cache-Control', 'public, max-age=31536000');
  next();
});

app.get('/*', prpl.makeHandler('.', config))

Choosing the right cache headers for your application can be complex. See Caching best practices & max-age gotchas for one starting point.

HTTP Errors

By default, if a 404 Not Found or other HTTP server error occurs, prpl-server will serve a minimal text/plain response. To serve custom errors, use prpl-server as a library with Express, set forwardErrors: true in your configuration object, and register an error-handling middleware after registering the prpl-server handler:

app.get('/*', prpl.makeHandler('.', {
  builds: [ ... ],
  forwardErrors: true
}));

app.use((err, req, res, next) => {
  if (err.status === 404) {
    res.status(404).sendFile('my-custom-404.html', {root: rootDir});
  } else {
    next();
  }
});

Rendering for Bots

Many bots don't execute JavaScript when processing your application. This can cause your application to not render correctly when crawled by some search engines, social networks, and link rendering bots.

One solution to this problem is Rendertron. Rendertron is a server which runs headless Chrome to render and serialize web pages for these bots, so all the content is contained in one network request. Use the --bot-proxy flag to instruct prpl-server to proxy requests from a known list of bots through a Rendertron server.

Note that you can also use the Rendertron middleware directly if you have a custom Express server.

Google App Engine Quickstart

Google App Engine is a managed server platform that supports Node.js. You can deploy prpl-server to App Engine standard environment with a few steps:

  1. Follow these instructions to set up a Google Cloud project and install the Google Cloud SDK. As instructed, run the gcloud init command to authenticate and choose your project ID.

  2. cd to the directory you want to serve (e.g. your app's build/ directory if you are using polymer-cli).

  3. Run npm init and follow the prompts to create your package.json.

  4. Run npm install --save prpl-server to add prpl-server as a dependency.

  5. Edit your package.json to add a start script. This is the command App Engine runs when your app starts. Configure prpl-server to listen on all hosts, and to redirect HTTP connections to HTTPS. You should also specify the version of Node your app requires via the engines section.

{
  "scripts": {
    "start": "prpl-server --host 0.0.0.0 --https-redirect"
  },
  "engines": {
    "node": "8.x.x"
  }
}
  1. Create an app.yaml file. This tells App Engine that you want to use Node.js in the App Engine standard environment:
runtime: nodejs8
  1. Run gcloud app deploy to deploy to your App Engine project. gcloud will tell you the URL your app is being served from. For next steps, check out the Node.js in the App Engine standard environment documentation.

More Repositories

1

polymer

Our original Web Component library.
HTML
22,033
star
2

polymer-starter-kit

A starting point for Polymer apps
JavaScript
2,457
star
3

pwa-starter-kit

Starter templates for building full-featured Progressive Web Apps from web components.
JavaScript
2,367
star
4

polymer-bundler

Moved to Polymer/tools monorepo
TypeScript
1,196
star
5

old-docs-site

Old Polymer site. Replaced by these repos: polymer-project.org, polymer-library-docs
HTML
1,022
star
6

shop

The Shop app
JavaScript
990
star
7

designer

Polymer Designer Tool
JavaScript
839
star
8

web-component-tester

Moved to Polymer/tools monorepo
TypeScript
566
star
9

polymer-cli

Moved to Polymer/tools monorepo
JavaScript
498
star
10

pwa-helpers

Small helper methods or mixins to help you build web apps.
TypeScript
440
star
11

tools

Polymer Tools Monorepo
TypeScript
430
star
12

news

Polymer News (Progress Web App Template)
HTML
263
star
13

polycasts

HTML
220
star
14

polyserve

Moved to Polymer/tools monorepo
TypeScript
192
star
15

polymer-analyzer

Moved to Polymer/tools monorepo
TypeScript
159
star
16

hn-polymer-2

Polymer Hacker News clone
HTML
158
star
17

polymer-modulizer

Moved to https://github.com/Polymer/tools/tree/master/packages/modulizer
HTML
143
star
18

polymer-build

Moved to Polymer/tools monorepo
TypeScript
103
star
19

polymer-decorators

TypeScript decorators for Polymer.
TypeScript
93
star
20

polymer-editor-service

Moved to Polymer/tools monorepo
TypeScript
76
star
21

vscode-plugin

Provides autocompletion, linting, and more for web components.
TypeScript
74
star
22

pwa-starter-kit-hn

TypeScript
70
star
23

lazy-imports

Declarative lazy HTML imports as a behavior.
HTML
63
star
24

polymer-css-build

JavaScript
40
star
25

polymer-linter

Moved to Polymer/tools monorepo
TypeScript
34
star
26

atom-plugin

Provides autocompletion, linting, and more for web components.
JavaScript
32
star
27

polymer-library-docs

Polymer library documentation site.
HTML
23
star
28

gen-typescript-declarations

Moved to Polymer/tools monorepo
TypeScript
21
star
29

wct-local

Moved to Polymer/tools monorepo
TypeScript
21
star
30

dom5

TypeScript
19
star
31

polymer-resin

XSS mitigation for Polymer webcomponents that uses safe html type contracts
JavaScript
18
star
32

wct-sauce

Moved to Polymer/tools monorepo
JavaScript
17
star
33

polymer-project.org

Polymer Project site & blog.
HTML
15
star
34

koa-node-resolve

Koa middleware that transforms Node package specifiers to relative paths
TypeScript
15
star
35

browser-capabilities

Moved to Polymer/tools monorepo
TypeScript
14
star
36

tattoo

Test All The Things Over and Over
TypeScript
12
star
37

koa-esm-transform

Middleware for Koa servers that transforms standard JavaScript modules to AMD modules for use with older browsers that don't support modules natively.
TypeScript
10
star
38

polymer-project-config

Moved to Polymer/tools monorepo
JavaScript
8
star
39

css-select-parse5-adapter

An adapter for the css-select package to allow querying of parse5 generated trees.
TypeScript
8
star
40

polymer-workspaces

Moved to https://github.com/Polymer/tools/tree/master/packages/workspaces
TypeScript
8
star
41

polymer.github.io

HTML
7
star
42

tools-sample-projects

Sample projects that Polymer tools should handle.
HTML
7
star
43

ar-logo

JavaScript
7
star
44

tools-common

Moved to Polymer/tools monorepo
JavaScript
6
star
45

benchmarks

Benchmarks for Polymer project libraries
JavaScript
6
star
46

plylog

A logger for the Polymer CLI toolchain
TypeScript
4
star
47

elements

A collection of elements made by the Polymer team
4
star
48

koa-karma-proxy

Simplified coordination of karma and upstream proxy server using the koa web framework.
TypeScript
4
star
49

mocha-suite-child

Runs mocha test suites defined in HTML documents and include the results as if they are part of the main context.
3
star
50

.allstar

2
star
51

tools-team

Issues for tools in general
2
star
52

polygit

Polygit
TypeScript
1
star
53

.github

1
star