Cowboy 2.0

The Shape of Things to Come

Loïc Hoguin (@lhoguin), Nine Nines

Cowboy changes... again?!

WTF man, don't you understand what stable means?

WTF is wrong with you?!

In this talk

  1. Why Cowboy 2
  2. Goals
  3. Low-level Cowboy
  4. High-level Cowboy
  5. Websocket
  6. Related projects

Why Cowboy 2

Cowboy 1 sucks

If you are watching this talk, you probably agree.

HTTP/2 requires a new model

HTTP/2 is concurrent, so Cowboy must also be.

Long term vision

Even if we are required to make small backward incompatible changes, overall the Cowboy 2 design will stand the test of time.

You can quote me on that.

Influences

Misultin, HTTP/2, Windows

Goals

Protocols supported

HTTP/2, HTTP/1.1, Websocket

HTTP/1.0, SPDY/3.1, SPDY/3

More power to users

  • Everything special processes (proc_lib/sys)
  • Pluggable low-level interface
  • Tons of options (like Windows!)

Simpler interface

  • Less, simpler callbacks
  • No more dealing with Req object
  • Extract, validate and convert input in one step
  • Maps. Maps everywhere!

Better code

All parsing code will be in Cowlib.

Cowboy will deal strictly with protocol logic and its own features.

Tests tests tests

  • Property based testing of Cowlib
  • Functional testing of Cowboy protocols and features

Low-level Cowboy

Performance

Do as little as possible, allocate as little as possible, and provide a pluggable interface for power users.

Use cases

  • Proxies (CONNECT or otherwise)
  • Frameworks
  • Low-level protocols (Websocket)
  • Hooks
  • Handling high demand resources early

The big picture

Connection → Protocol → Streams

Connection

  • cowboy_clear
  • cowboy_tls

Listener startup

  • cowboy:start_clear(Name, Nb, TransOpts, ProtoOpts)
  • cowboy:start_tls(Name, Nb, TransOpts, ProtoOpts)

Choice of protocol

cowboy_clear → cowboy_http

cowboy_tls → ALPN → Protocol

  • cowboy_http
  • cowboy_http2
  • cowboy_spdy

ALPN?

  1. Client advertises list of protocols it supports
  2. Server chooses protocol and informs client

Erlang/OTP 18+

Protocol modules

  • Common interface
  • Non-blocking
  • Handles system messages
  • Able to act as a supervisor if stream-based

Protocol upgrades

Same mechanism for:

  • Upgrading from HTTP/1.1 to HTTP/2
  • Upgrading from HTTP/1.1 to Websocket
  • Upgrading HTTP/1.1 connections from TCP to TLS

Streams

A stream is an HTTP request/response pair identified by a unique stream identifier.

A stream can be initiated by the server using a promise.

Stream-based protocols

  • cowboy_http
  • cowboy_http2
  • cowboy_spdy
  • (cowboy_coap?)
  • NOT cowboy_websocket

Stream handler

  • init(ID, IsFin, Method, Scheme, Host, Path, Headers, Opts)
  • data(ID, IsFin, Data, State)
  • info(ID, Msg, State)
  • terminate(ID, Reason, State)

Stream-specific messages

  • Messages are filtered per-stream
  • {{Handler, ID}, Msg}
  • {{Handler, ID}, From, Msg}

Stream commands

  • {response, IsFin, StatusCode, Headers}
  • {data, IsFin, Data}
  • {promise, Method, Scheme, Authority, Path, Headers}
  • {flow, auto | Size}
  • {spawn, Pid}
  • {upgrade, Mod, Opts}

Default stream handler

  • Creates a new process per stream
  • Communicates with process using Cowboy stream protocol
  • High-level Cowboy

One process per request

  • High-level Cowboy uses 1 proc/conn + 1 proc/stream
  • BUT
  • Low-level Cowboy uses 1 proc/conn only

Cowboy stream protocol

  • {response_header, Key, Value}
  • {response_cookie, Key, Value, Opts}
  • {response_body, Body}
  • {response, IsFin, StatusCode, Headers}
  • {data, IsFin, Data}
  • {promise, Method, Scheme, Authority, Path, Headers}
  • {data_request, Size}

Layered stream handlers

  • A stream handler can call another stream handler
  • Use handler-specific options to define the next layer
  • This makes Cowboy 1 hooks worthless

High-level Cowboy

Convenience

Pie tastes good, right? Just like Cowboy 2.

Favor convenience, elegance and simplicity. No pitfalls. Straightforward.

The big picture

Middlewares → User handlers

Default: cowboy_router → cowboy_handler

Middlewares

No more 'error' tuple; ok, suspend, stop

Routing

I would like to solve the fact that routing rules are copied to all connection processes. Perhaps ets?

Reverse routing

Required for better HATEOAS support.

Give module name and bindings, get URL.
Add query string to URL (optional).

Constraints

Use constraints all across high-level Cowboy.

Improve error handling interface, add human errors.

Handlers

Unify init and terminate callbacks

Simplify init return value: {ok | Mod, State}

Do everything in init/2

REST handlers

Add behaviour with optional callbacks

Remove known_content_type callback

Req object

Immutable

3 levels of access to values:

  • Raw value
  • Parsed value
  • Matched value

Match functions

Extract, validate and convert values in one step.


#{lang := Lang} = cowboy_req:match_qs(
	[{lang, nonempty, <<"en-US">>}], Req)
	

Additional notes

Handler suffix becomes _h

Settle on 'stop' instead of 'shutdown' or 'halt'

Cowlib provides parsers for nearly everything

Websocket

Upgrade

  • From stream handler
  • From request process

Connection process switches to Websocket protocol

Features

Websocket permessage-deflate support added.

Websocket UTF-8 validation optimized.
An option to disable validation will be added.

Websocket handlers

  • No more websocket_init
  • No more websocket_terminate
  • Optional terminate
  • No keeping track of Req

Related projects

Ranch 2

  • Merge acceptor and supervisor functionality
  • Use the async accept mechanism

Gun

Gun is an asynchronous HTTP client with support for HTTP/1.1, HTTP/2, SPDY/3 and Websocket, designed for long-running connections.

Erlang.mk

Erlang.mk is a Makefile based build tool that just works.

No Makefile knowledge required to use it

Why Erlang.mk

My users need a build tool that actually works.

Convince me

  • Compatible with a lot more projects than rebar
  • Dependencies can be in any language (C, Javascript...)
  • It's just a text file

Complexity comparison

Rebar featureErlang.mk equivalent
rebar.configvariables
rebar.config.scriptvariables and/or rules
rebar hookrules
rebar2 pluginrules
rebar3 pluginrules

Erlang.mk index

Getting close to 450 projects

DEPS = cowboy cpg erlydtl riak_core

All packages 1/6

aberth active aleppo alog annotations antidote apns azdht backoff barrel basho_bench bcrypt beam beanstalk bear bertconf bifrost binpp bisect bitcask bitstore bootstrap boss_db boss bson bullet cache cake carotene cberl cecho cferl chaos_monkey check_node chronos classifier clique cloudi_core cloudi_service_* cluster_info color confetti couchbeam couch covertool cowboy cowdb cowlib cpg cqerl cr cuttlefish damocles debbie decimal detergent dh_date dhtcrawler dirbusterl dispcount dlhttpc dns dnssd dtl dynamic_compile e2 eamf eavro ecapnp econfig edate edgar edis edns

All packages 2/6

edown eep_app eep efene eganglia egeoip ehsa ejabberd ej ekaf elarm eleveldb elli elvis emagick emysql enm entop epcap eper epgsql episcina eplot epocxy epubnub eqm eredis eredis_pool erlang_cep erlang_js erlang_localtime erlang_smtp erlasticsearch erlastic_search erlbrake erlcloud erlcron erldb erldis erldns erldocker erlfsmon erlgit erlguten erlmc erlmongo erlog erlpass erlport erlsha2 erlsh erlsom erl_streams erlubi erlvolt erlware_commons erlydtl errd erserve erwa espec estatsd etap etest etest_http etoml eunit_formatters eunit euthanasia evum exec exml exometer exs1024 exs64 exsplus116 exsplus128 ezmq ezmtp

All packages 3/6

fast_disk_log feeder fix flower fn folsom_cowboy folsom folsomite fs fuse gcm gcprof geas geef gen_cycle gen_icmp gen_nb_server gen_paxos gen_smtp gen_tracker gen_unix getopt gettext giallo gin gitty gold_fever gpb gproc grapherl gun hackney hamcrest hanoidb hottub hyper ibrowse ierlang iota ircd irc_lib iris iso8601 itweet jerg jesse jiffy jiffy_v jobs joxa jsonerl json jsonpath json_rec jsonx jsx kafka kai katja kdht kinetic kjell kraken kucumberl kvc kvlists kvs lager_amqp_backend lager lager_syslog lambdapad lasp lasse ldap lethink lfe ling live lmq locker locks log4erl lol lucid luerl luwak lux mad mavg mcd mcerlang mc_erl meck

All packages 4/6

mekao memo merge_index merl mimetypes mixer mochiweb mochiweb_xpath mockgyver modlib mongodb mongooseim moyo msgpack mustache myproto mysql n2o nat_upnp neo4j neotoma newrelic nifty nitrogen_core nkbase nkdocker nkpacket nodefinder nprocreg oauth2c oauth2 oauth of_protocol openflow openid openpoker pal parse_trans parsexml pegjs percept2 pgsql pkgx pkt plain_fsm plumtree pmod_transform pobox ponos poolboy pooler poxa pqueue procket proper props protobuffs psycho ptrackerl purity push_service qdate qrcode quest rabbit_exchange_type_riak rack radierl rafter ranch

All packages 5/6

rbeacon rebar rebus rec2json recon record_info redgrid redo relx resource_discovery restc rfc4627_jsonrpc riakc riak_core riak_dt riak_ensemble riakhttpc riak_kv riaknostic riak_pg riak_pipe riakpool riak_sysmon riak_test rivus_cep rlimit safetyvalve seestar service setup sext sfmt sgte sheriff shotgun sidejob sieve sighandler simhash simple_bridge simple_oauth2 skel social spapi_router sqerl srly sshrpc stable statebox statebox_riak statman statsderl stdinout_pool stockdb stripe surrogate swab swarm switchboard sync syntaxerl syslog taskforce tddreloader tempo ticktick tinymq tinymt traffic_tools trane transit trie

All packages 6/6

triq tunctl twerl twitter_erlang ucol_nif unicorn unsplit uuid ux vert verx vmq_acl vmq_bridge vmq_graphite vmq_passwd vmq_server vmq_snmp vmq_systree vmstats walrus webmachine websocket_client worker_pool wrangler wsock xref_runner yamerl yamler yaws zab_engine zeta zippers zlists zraft_lib zucchini

Erlang.mk plans 1/2

  1. Compile everything
  2. Keep track of versions
  3. Provide curated packages

Erlang.mk plans 2/2

  1. Add support for LFE, Elixir projects
  2. Add Concuerror, Chaos Monkey, Smother, RefactorErl...
  3. Generate the .app file without a .app.src

Everything in the Makefile

  • PROJECT → application name
  • PROJECT_DESCRIPTION → description
  • PROJECT_VERSION → vsn
  • PROJECT_ID → id
  • PROJECT_TYPE → mod, registered
  • PROJECT_REGISTERED → registered
  • PROJECT_ENV or PROJECT_ENV_FILE → env
  • modules list automatically filled
  • applications list automatically filled from DEPS

Everything optional

All the variables that ultimately build the .app file are either optional or automatically defined when bootstrapping.

Adding a dependency becomes a one step process.

Starting up goals

  • mkdir kitty; cd kitty
  • wget $ERLANG_MK_URL
  • make -f bootstrap bootstrap-rel
  • append DEPS = cowboy cpg erlydtl riak_core
  • make run

Starting up later goals

  • erlang-mk new kitty
  • append DEPS = cowboy cpg erlydtl riak_core
  • make run

Erlang.mk on Windows

Make 4 makes supporting Windows possible without needing the whole Unix toolchain.

Before this there are much bigger Windows issues to solve to make the experience smooth, unrelated to Erlang.mk

The Erlanger Playbook

This book is the missing developer manual. It contains advice from my experience working with Cowboy.

Price: 50€

Ebook preorder available soon on ninenines.eu

Want to buy it today and get it right now? Come talk to me or send an email later at essen@ninenines.eu

Terminate

tl;dr

Cowboy 2's design is long term.

Questions?

To look at an early release of the code in this talk:
tag 2.0.0-pre.2

To buy the book, access projects:
ninenines.eu

To follow my exciting adventures:
@lhoguin on Twitter

To ask questions, talk about hats:
#ninenines on Freenode