From 2bddf410f382f9ad7c71f5449918c5ac43638cb3 Mon Sep 17 00:00:00 2001 From: Gary Rennie Date: Wed, 26 Jul 2017 09:11:06 +0100 Subject: [PATCH] Example for server push usage This example uses SSL as it is required in most browsers for server push. --- examples/ssl_push/Makefile | 9 ++ examples/ssl_push/README.asciidoc | 122 ++++++++++++++++++ examples/ssl_push/priv/ssl/cowboy-ca.crt | 16 +++ examples/ssl_push/priv/ssl/server.crt | 17 +++ examples/ssl_push/priv/ssl/server.key | 15 +++ examples/ssl_push/priv/static_files/style.css | 3 + examples/ssl_push/relx.config | 2 + examples/ssl_push/src/ssl_push_app.erl | 30 +++++ examples/ssl_push/src/ssl_push_sup.erl | 23 ++++ examples/ssl_push/src/toppage_handler.erl | 20 +++ 10 files changed, 257 insertions(+) create mode 100644 examples/ssl_push/Makefile create mode 100644 examples/ssl_push/README.asciidoc create mode 100644 examples/ssl_push/priv/ssl/cowboy-ca.crt create mode 100644 examples/ssl_push/priv/ssl/server.crt create mode 100644 examples/ssl_push/priv/ssl/server.key create mode 100644 examples/ssl_push/priv/static_files/style.css create mode 100644 examples/ssl_push/relx.config create mode 100644 examples/ssl_push/src/ssl_push_app.erl create mode 100644 examples/ssl_push/src/ssl_push_sup.erl create mode 100644 examples/ssl_push/src/toppage_handler.erl diff --git a/examples/ssl_push/Makefile b/examples/ssl_push/Makefile new file mode 100644 index 000000000..2e525bf23 --- /dev/null +++ b/examples/ssl_push/Makefile @@ -0,0 +1,9 @@ +PROJECT = ssl_push +PROJECT_DESCRIPTION = Cowboy HTTP/2 push example +PROJECT_VERSION = 1 + +DEPS = cowboy +LOCAL_DEPS = ssl +dep_cowboy_commit = master + +include ../../erlang.mk diff --git a/examples/ssl_push/README.asciidoc b/examples/ssl_push/README.asciidoc new file mode 100644 index 000000000..c1a325a39 --- /dev/null +++ b/examples/ssl_push/README.asciidoc @@ -0,0 +1,122 @@ += HTTP/2 push example + +To try this example, you need GNU `make` and `git` in your PATH. + +To build and run the example, use the following command: + +[source,bash] +$ make run + +Then point your browser to https://localhost:8443 + +You will need to temporarily trust the root certificate authority, +which can also be found in `priv/ssl/cowboy-ca.crt`. + +Recent browsers will communicate using HTTP/2. Older browsers +will use HTTP/1.1. + +== HTTP/1.1 example output + +[source,bash] +---- +$ curl --cacert priv/ssl/cowboy-ca.crt -i https://localhost:8443 +HTTP/1.1 200 OK +content-length: 117 +content-type: text/html +date: Wed, 26 Jul 2017 08:07:13 GMT +server: Cowboy + + + + + + +

Hello world!

+ +---- + +== HTTP/2 example output + +[source,bash] +---- +$ nghttp -v https://localhost:8443 +[ 0.001] Connected +The negotiated protocol: h2 +[ 0.014] recv SETTINGS frame + (niv=0) +[ 0.014] send SETTINGS frame + (niv=2) + [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] + [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] +[ 0.014] send SETTINGS frame + ; ACK + (niv=0) +[ 0.014] send PRIORITY frame + (dep_stream_id=0, weight=201, exclusive=0) +[ 0.014] send PRIORITY frame + (dep_stream_id=0, weight=101, exclusive=0) +[ 0.014] send PRIORITY frame + (dep_stream_id=0, weight=1, exclusive=0) +[ 0.014] send PRIORITY frame + (dep_stream_id=7, weight=1, exclusive=0) +[ 0.014] send PRIORITY frame + (dep_stream_id=3, weight=1, exclusive=0) +[ 0.014] send HEADERS frame + ; END_STREAM | END_HEADERS | PRIORITY + (padlen=0, dep_stream_id=11, weight=16, exclusive=0) + ; Open new stream + :method: GET + :path: / + :scheme: https + :authority: localhost:8443 + accept: */* + accept-encoding: gzip, deflate + user-agent: nghttp2/1.19.0-DEV +[ 0.014] recv SETTINGS frame + ; ACK + (niv=0) +[ 0.014] recv (stream_id=13) :authority: localhost:8443 +[ 0.014] recv (stream_id=13) :method: GET +[ 0.014] recv (stream_id=13) :path: /static/style.css +[ 0.014] recv (stream_id=13) :scheme: https +[ 0.014] recv (stream_id=13) accept: text/css +[ 0.014] recv PUSH_PROMISE frame + ; END_HEADERS + (padlen=0, promised_stream_id=2) +[ 0.014] recv (stream_id=13) :status: 200 +[ 0.014] recv (stream_id=13) content-length: 117 +[ 0.014] recv (stream_id=13) content-type: text/html +[ 0.014] recv (stream_id=13) date: Wed, 26 Jul 2017 08:08:03 GMT +[ 0.014] recv (stream_id=13) server: Cowboy +[ 0.014] recv HEADERS frame + ; END_HEADERS + (padlen=0) + ; First response header + + + + + +

Hello world!

+ +[ 0.014] recv DATA frame + ; END_STREAM +[ 0.015] recv (stream_id=2) :status: 200 +[ 0.015] recv (stream_id=2) content-length: 21 +[ 0.015] recv (stream_id=2) content-type: text/css +[ 0.015] recv (stream_id=2) date: Wed, 26 Jul 2017 08:08:03 GMT +[ 0.015] recv (stream_id=2) etag: "2003476335" +[ 0.015] recv (stream_id=2) last-modified: Wed, 26 Jul 2017 08:01:45 GMT +[ 0.015] recv (stream_id=2) server: Cowboy +[ 0.015] recv HEADERS frame + ; END_HEADERS + (padlen=0) + ; First push response header +h1 { + color: red; +} +[ 0.015] recv DATA frame + ; END_STREAM +[ 0.015] send GOAWAY frame + (last_stream_id=2, error_code=NO_ERROR(0x00), opaque_data(0)=[]) +---- diff --git a/examples/ssl_push/priv/ssl/cowboy-ca.crt b/examples/ssl_push/priv/ssl/cowboy-ca.crt new file mode 100644 index 000000000..a35ac390a --- /dev/null +++ b/examples/ssl_push/priv/ssl/cowboy-ca.crt @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICeDCCAeGgAwIBAgIJAOvpU0y2e5J4MA0GCSqGSIb3DQEBBQUAMFUxCzAJBgNV +BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczETMBEGA1UECgwKTmluZSBOaW5lczEPMA0G +A1UECwwGQ293Ym95MRAwDgYDVQQDDAdST09UIENBMB4XDTEzMDIyODA1MTAwMVoX +DTMzMDIyMzA1MTAwMVowVTELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMRMw +EQYDVQQKDApOaW5lIE5pbmVzMQ8wDQYDVQQLDAZDb3dib3kxEDAOBgNVBAMMB1JP +T1QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMzmY7Us06yjyUbpqwPx +Iv+xh/g3V7we07ClC9GEYnvr3OQvdA1jFEHccMBUUjRoQ8DPd6uSyK5UkixABs08 +Tt5B3VsnGKr0DIN+IO4SN2PkmBqIU/BN3KdcwN65YNr3iM0KsKWeFtAZdYx4CakX +7REbO0wjK20AH3xSBn3uFGiBAgMBAAGjUDBOMB0GA1UdDgQWBBRKfZ8KF2jlLBDm +NL6IuEuGY0pdbzAfBgNVHSMEGDAWgBRKfZ8KF2jlLBDmNL6IuEuGY0pdbzAMBgNV +HRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAG1I0kBxXiLkM1b7rl2zPLizREYg +1m+ajb6rWzPOBg6TXjv58Be+H4tqoHIL/M/crixew5emftBkuAGjiKMhbIokjvan +aPTCV8U6HHvNvz9c68HpESWbd+56cHqfsS5XCKp1OpW5tbL2UQYpFKMP4qmbv3Ea +pBfPPmSFMBb1i2AI +-----END CERTIFICATE----- diff --git a/examples/ssl_push/priv/ssl/server.crt b/examples/ssl_push/priv/ssl/server.crt new file mode 100644 index 000000000..0bdfaed01 --- /dev/null +++ b/examples/ssl_push/priv/ssl/server.crt @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICpTCCAg6gAwIBAgIJAOvpU0y2e5J5MA0GCSqGSIb3DQEBBQUAMFUxCzAJBgNV +BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczETMBEGA1UECgwKTmluZSBOaW5lczEPMA0G +A1UECwwGQ293Ym95MRAwDgYDVQQDDAdST09UIENBMB4XDTEzMDIyODA1MjMzNFoX +DTMzMDIyMzA1MjMzNFowVzELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMRMw +EQYDVQQKDApOaW5lIE5pbmVzMQ8wDQYDVQQLDAZDb3dib3kxEjAQBgNVBAMMCWxv +Y2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAzbW1GjECzHUc/WST +qLiAGqjCNccR5saVS+yoz2SPRhpoyf0/qBrX5BY0tzmgozoTiRfE4wCiVD99Cc+D +rp/FM49r4EpZdocIovprmOmv/gwkoj95zaA6PKNn1OdmDp2hwJsX2Zm3kpbGUZTx +jDkkccmgUb4EjL7qNHq7saQtivUCAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgB +hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE +FB6jTEIWI8T1ckORA4GezbyYxtbvMB8GA1UdIwQYMBaAFEp9nwoXaOUsEOY0voi4 +S4ZjSl1vMA0GCSqGSIb3DQEBBQUAA4GBACMboVQjrx8u/fk3gl/sR0tbA0Wf/NcS +2Dzsy2czndgVUAG4Sqb+hfgn0dqAyUKghRrj3JDcYxYksGPIklDfPzZb7yJ39l16 +6x5ZiIzhp8CAVdPvRxRznw5rZwaXesryXu1jVSZxTr3MYZdkG6KaAM0t90+YlGLZ +UG8fAicx0Bf+ +-----END CERTIFICATE----- diff --git a/examples/ssl_push/priv/ssl/server.key b/examples/ssl_push/priv/ssl/server.key new file mode 100644 index 000000000..b6f737421 --- /dev/null +++ b/examples/ssl_push/priv/ssl/server.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDNtbUaMQLMdRz9ZJOouIAaqMI1xxHmxpVL7KjPZI9GGmjJ/T+o +GtfkFjS3OaCjOhOJF8TjAKJUP30Jz4Oun8Uzj2vgSll2hwii+muY6a/+DCSiP3nN +oDo8o2fU52YOnaHAmxfZmbeSlsZRlPGMOSRxyaBRvgSMvuo0eruxpC2K9QIDAQAB +AoGAaD85c/h6bpq7Aj7CBbLaWKhFI3OqwsTITB22vsM7SE+B4zsP02UnG1OVi3UM +zytTUxpUkKV1njQ+bYZYOVqGWF4Up8tTqUglHn0FTPok1AIemELWtz3sXvdSHC1T +lqvFBAZ9kibn13qGyVOiyCFaMwfOM/05RvV7p3jfUMTWnNECQQDs7yCJZ8Ol8MyH +TGZzvkjoN2zg1KwmTbSD1hkP6QAJtPdRuqFbjlEru0/PefgOXsWLRIa3/3v0qw2G +xGkV6AXTAkEA3kNbFisqUydjPnZIYv/P6SvPdUimHJEjXbAbfNfzS9dzszrOVJd2 +XqGH7z5yzjoH3IyaIMW8GnubVzGDSjrHFwJAKSU5vELlygpwKkrNO+pelN0TLlQg +dSJnZ8GlZorq88SWcn37iX/EftivenNO7YftvEqxLoDSkOGnnrC7Iw/A+wJBAIEe +L/QY72WPJCBNJpAce/PA96vyoE1II3txqwZDjZspdpVQPDz4IFOpEwbxCFC1dYuy +Qnd3Z2cbF4r3wIWGz9ECQQCJGNhUNtY+Om1ELdqPcquxE2VRV/pucnvJSTKwyo2C +Rvm6H7kFDwPDuN23YnTOlTiho0zzCkclcIukhIVJ+dKz +-----END RSA PRIVATE KEY----- diff --git a/examples/ssl_push/priv/static_files/style.css b/examples/ssl_push/priv/static_files/style.css new file mode 100644 index 000000000..adc68fa6a --- /dev/null +++ b/examples/ssl_push/priv/static_files/style.css @@ -0,0 +1,3 @@ +h1 { + color: red; +} diff --git a/examples/ssl_push/relx.config b/examples/ssl_push/relx.config new file mode 100644 index 000000000..f6f8dc0b0 --- /dev/null +++ b/examples/ssl_push/relx.config @@ -0,0 +1,2 @@ +{release, {ssl_push_example, "1"}, [ssl_push]}. +{extended_start_script, true}. diff --git a/examples/ssl_push/src/ssl_push_app.erl b/examples/ssl_push/src/ssl_push_app.erl new file mode 100644 index 000000000..03e9cac14 --- /dev/null +++ b/examples/ssl_push/src/ssl_push_app.erl @@ -0,0 +1,30 @@ +%% Feel free to use, reuse and abuse the code in this file. + +%% @private +-module(ssl_push_app). +-behaviour(application). + +%% API. +-export([start/2]). +-export([stop/1]). + +%% API. + +start(_Type, _Args) -> + Dispatch = cowboy_router:compile([ + {'_', [ + {"/static/[...]", cowboy_static, {priv_dir, ssl_push, "static_files"}}, + {"/", toppage_handler, []} + ]} + ]), + PrivDir = code:priv_dir(ssl_push), + {ok, _} = cowboy:start_tls(https, [ + {port, 8443}, + {cacertfile, PrivDir ++ "/ssl/cowboy-ca.crt"}, + {certfile, PrivDir ++ "/ssl/server.crt"}, + {keyfile, PrivDir ++ "/ssl/server.key"} + ], #{env => #{dispatch => Dispatch}}), + ssl_push_sup:start_link(). + +stop(_State) -> + ok. diff --git a/examples/ssl_push/src/ssl_push_sup.erl b/examples/ssl_push/src/ssl_push_sup.erl new file mode 100644 index 000000000..0f50fc5d1 --- /dev/null +++ b/examples/ssl_push/src/ssl_push_sup.erl @@ -0,0 +1,23 @@ +%% Feel free to use, reuse and abuse the code in this file. + +%% @private +-module(ssl_push_sup). +-behaviour(supervisor). + +%% API. +-export([start_link/0]). + +%% supervisor. +-export([init/1]). + +%% API. + +-spec start_link() -> {ok, pid()}. +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +%% supervisor. + +init([]) -> + Procs = [], + {ok, {{one_for_one, 10, 10}, Procs}}. diff --git a/examples/ssl_push/src/toppage_handler.erl b/examples/ssl_push/src/toppage_handler.erl new file mode 100644 index 000000000..119578b8d --- /dev/null +++ b/examples/ssl_push/src/toppage_handler.erl @@ -0,0 +1,20 @@ +%% Feel free to use, reuse and abuse the code in this file. + +%% @doc Hello world handler. +-module(toppage_handler). + +-export([init/2]). + +init(Req0, Opts) -> + cowboy_req:push("/static/style.css", #{<<"accept">> => <<"text/css">>}, Req0), + Req = cowboy_req:reply(200, #{ + <<"content-type">> => <<"text/html">> + }, <<" + + + + +

Hello world!

+ +">>, Req0), + {ok, Req, Opts}.