summaryrefslogtreecommitdiffstats
path: root/talks/tale-of-2.0-cowboy/index.html
diff options
context:
space:
mode:
Diffstat (limited to 'talks/tale-of-2.0-cowboy/index.html')
-rw-r--r--talks/tale-of-2.0-cowboy/index.html446
1 files changed, 446 insertions, 0 deletions
diff --git a/talks/tale-of-2.0-cowboy/index.html b/talks/tale-of-2.0-cowboy/index.html
new file mode 100644
index 00000000..4861dd89
--- /dev/null
+++ b/talks/tale-of-2.0-cowboy/index.html
@@ -0,0 +1,446 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>A tale of 2.0 Cowboy</title>
+
+ <meta name="description" content"Cowboy 2 EUC 2017 talk">
+ <meta name="author" content="Loïc Hoguin">
+
+ <link rel="stylesheet" href="css/reveal.css">
+ <link rel="stylesheet" href="css/theme/black.css">
+ </head>
+ <body>
+ <div class="reveal">
+ <div class="slides">
+
+<section>
+ <h1>A tale of 2.0 Cowboy</h1>
+ <p>I'm a poor lonesome Cowboy,<br/>and a long way from home...</p>
+ <p><small>Loïc Hoguin (<a href="https://twitter.com/lhoguin">@lhoguin</a>), <a href="https://ninenines.eu">Nine Nines</a></small></p>
+</section>
+
+<section>
+ <h1>Confessions</h1>
+</section>
+
+<section>
+ <section>
+ <h2>Slow and steady</h2>
+ <img src="img/turtle-racing.jpg"/>
+ </section>
+
+ <section>
+ <h2>Life happened</h2>
+ <p>Literally</p>
+ </section>
+
+ <section>
+ <ul>
+ <li>Modifying an existing project is hard</li>
+ <li>Starting from scratch is easy (at first)</li>
+ </ul>
+ </section>
+
+ <section>
+ <h2>Many changes</h2>
+ <ul>
+ <li>Project core changed the most</li>
+ <li>Interface received numerous tweaks</li>
+ <li>New testing methodology</li>
+ <li>More detailed documentation</li>
+ </ul>
+ </section>
+
+ <section>
+ <h2>HTTP is complicated</h2>
+ <p>HTTP/2 even more so</p>
+ </section>
+</section>
+
+<section>
+ <h1>Lessons learned</h1>
+</section>
+
+<section>
+ <section>
+ <h2>Active supervisors</h2>
+ <img src="img/river-delta.jpg"/>
+ </section>
+
+ <section>
+ <ul>
+ <li>One process... <ul>
+ <li>Tells another process... <ul>
+ <li>To start a third process</li>
+ </ul></li>
+ </ul></li>
+ </ul>
+ </section>
+
+ <section>
+ <h3>Solutions</h3>
+ <ul>
+ <li>Let the supervisor act</li>
+ <li>Make worker processes supervise</li>
+ </ul>
+ </section>
+
+ <section>
+ <h3>Example: acceptor supervisor</h3>
+ <ul>
+ <li>Basically a supervisor with an <code>info/3</code> callback <ul>
+ <li>Accept connection</li>
+ <li>Spawn a process for that connection</li>
+ <li>Supervise the process</li>
+ </ul></li>
+ <li>Coming in Ranch 2.0</li>
+ </ul>
+ </section>
+
+ <section>
+ <h3>Example: connection workers</h3>
+ <ul>
+ <li>Process manages connection and related workers</li>
+ <li>Connection and request processes in Cowboy 2.0</li>
+ <li>Many implications</li>
+ </ul>
+ </section>
+</section>
+
+<section>
+ <section>
+ <h2>List of commands</h2>
+ <img src="img/to-do-list.png"/>
+ </section>
+
+ <section>
+ <pre><code data-trim data-noescape>
+ {[
+ {headers, fin, 100, Headers},
+ {flow, 64000},
+ {spawn, Pid, 5000}
+ ], State}.
+ </code></pre>
+ </section>
+
+ <section>
+ <h3>Examples</h3>
+ <ul>
+ <li><code>gen_statem</code></li>
+ <li>Cowboy 2.0 stream handlers</li>
+ <li>Cowboy 2.1+ Websocket handlers</li>
+ </ul>
+ </section>
+
+ <section>
+ <pre><code data-trim data-noescape>
+init(StreamID, Req, Opts) → {Commands, State}
+
+data(StreamID, IsFin, Data, State) → {Commands, State}
+
+info(StreamID, Msg, State) → {Commands, State}
+
+terminate(StreamID, Reason, State) → _
+
+early_error(StreamID, Reason, PartialReq, Resp, Opts) → Resp
+ </code></pre>
+ </section>
+</section>
+
+<section>
+ <section>
+ <h2>Callback chains</h2>
+ <img src="img/chain.jpg"/>
+ </section>
+
+ <section>
+ <pre><code data-trim data-noescape>
+#{
+ middlewares ⇒
+ [cowboy_router, cowboy_handler],
+ stream_handlers ⇒
+ [cowboy_compress_h, cowboy_stream_h]
+}
+ </code></pre>
+ </section>
+
+ <section>
+ <h3>Middleware</h3>
+ <pre><code data-trim data-noescape>
+ execute(Req0, Env0) →
+ case do_something(Req0, Env0) of
+ {ok, Req, Env} →
+ {ok, Req, Env};
+ error →
+ {stop, Req0}
+ end.
+ </code></pre>
+ </section>
+
+ <section>
+ <h3>Stream handler 1/2</h3>
+ <pre><code data-trim data-noescape>
+data(StreamID, IsFin, Data, State=#state{next=Next0}) →
+ {Commands0, Next} = cowboy_stream:data(
+ StreamID, IsFin, Data, Next0),
+ fold(Commands0, State#state{next=Next}).
+ </code></pre>
+ </section>
+
+ <section>
+ <h3>Stream handler 2/2</h3>
+ <pre><code data-trim data-noescape>
+data(StreamID, IsFin, Data0, State=#state{next=Next0}) →
+ Data = do_something(Data0),
+ {Commands0, Next} = cowboy_stream:data(
+ StreamID, IsFin, Data, Next0),
+ {Commands0, State#state{next=Next}}.
+ </code></pre>
+ </section>
+</section>
+
+<section>
+ <section>
+ <h2>Receive loop</h2>
+ <img src="img/mailboxes.jpg"/>
+ </section>
+
+ <section>
+ <pre><code data-trim data-noescape>
+ loop() →
+ receive
+ Msg →
+ do_something(Msg),
+ loop()
+ end.
+ </code></pre>
+ </section>
+
+ <section>
+ <h3>Loop handlers</h3>
+ <ul>
+ <li>Native support for server-sent events</li>
+ <li>Switch to loop handlers from <code>cowboy_rest</code></li>
+ <li>After Cowboy 2.0</li>
+ </ul>
+ </section>
+</section>
+
+<section>
+ <section>
+ <h2>Error 3-tuples</h2>
+ <img src="img/error-message.jpg"/>
+ </section>
+
+ <section>
+ <pre><code data-trim data-noescape>
+{error, Reason}
+
+Reason =
+ {connection_error,
+ protocol_error,
+ 'The preface sequence must be followed
+ by a SETTINGS frame. (RFC7540 3.5)'}
+ </code></pre>
+ </section>
+</section>
+
+<section>
+ <section>
+ <h2>Options map</h2>
+ <img src="img/treasure-map.jpg"/>
+ </section>
+
+ <section>
+ <pre><code data-trim data-noescape>
+uri(Req :: cowboy_req:req()) → uri(Req, #{})
+uri(Req :: cowboy_req:req(), Opts) → URI :: iodata()
+
+Opts :: #{
+ scheme ⇒ iodata() | undefined,
+ host ⇒ iodata() | undefined,
+ port ⇒ inet:port_number() | undefined,
+ path ⇒ iodata() | undefined,
+ qs ⇒ iodata() | undefined,
+ fragment ⇒ iodata() | undefined
+}
+ </code></pre>
+ </section>
+
+ <section>
+ <pre><code data-trim data-noescape>
+ read_body(Req :: cowboy_req:req())
+ → read_body(Req, #{})
+
+ read_body(Req :: cowboy_req:req(), Opts)
+ → {ok, Data :: binary(), Req}
+ | {more, Data :: binary(), Req}
+
+ Opts :: #{
+ length ⇒ non_neg_integer(),
+ period ⇒ non_neg_integer(),
+ timeout ⇒ timeout()
+ }
+ </code></pre>
+ </section>
+
+ <section>
+ <h3>Required values</h3>
+ <p>Use <code>:=</code> instead of <code>=&gt;</code></p>
+ </section>
+</section>
+
+<section>
+ <section>
+ <h2>Better tests</h2>
+ <img src="img/bridge-collapsing.jpg"/>
+ </section>
+
+ <section>
+ <h3>Standards compliance</h3>
+ <p>For RFCs, at least one test per relevant MUST/SHOULD.</p>
+ <p>Document in the test suite what couldn't be tested.</p>
+ </section>
+
+ <section>
+ <h3>Features</h3>
+ <p>Every function, argument, option must have a test.</p>
+ </section>
+
+ <section>
+ <h3>Some tips</h3>
+ <ul>
+ <li>Unit tests are not interesting</li>
+ <li>Always run test cases in parallel</li>
+ <li>Short, successful tests must not output errors</li>
+ <li>Test cases must be documented</li>
+ </ul>
+ </section>
+
+ <section>
+ <img src="img/documented-tests.png"/>
+ </section>
+</section>
+
+<section>
+ <section>
+ <h2>Better docs</h2>
+ <img src="img/huge-library.jpg"/>
+ </section>
+
+ <section>
+ <p>Taking lessons from PHP</p>
+ <p><a href="https://ninenines.eu/docs/en/cowboy/2.0/manual/cowboy_req.read_body/">Example</a></p>
+ </section>
+
+ <section>
+ <h3>Highlights</h3>
+ <ul>
+ <li>One (man) page per module</li>
+ <li>One (man) page per function</li>
+ <li>Description, arguments, return value, changelog, examples...</li>
+ </ul>
+ </section>
+</section>
+
+<section>
+ <h1>Cowboy 2.0+</h1>
+</section>
+
+<section>
+ <section>
+ <h2>When?</h2>
+ <ul>
+ <li>Cowboy 2.0-rc.1: Summer 2017 <ul>
+ <li>After OTP 20 is released</li>
+ </ul></li>
+ <li>Cowboy 2.0: 2017</li>
+ <li>Requires Erlang/OTP 19+ (sorry!)</li>
+ </ul>
+ </section>
+
+ <section>
+ <h3>What's left?</h3>
+ <ul>
+ <li>Return multipart headers as a map</li>
+ <li>Constraints 2.0</li>
+ </ul>
+ </section>
+</section>
+
+<section>
+ <section>
+ <h2>New users</h2>
+ <ul>
+ <li>RabbitMQ (1.1, for now)</li>
+ <li>Zotonic</li>
+ <li>Barrel</li>
+ </ul>
+ </section>
+</section>
+
+<section>
+ <section>
+ <h2>Related projects</h2>
+ <ul>
+ <li>Ranch 1.4: Out now!</li>
+ <li>Gun 1.0: 2017</li>
+ <li>Cowlib 2.0+: Adding documentation</li>
+ <li>Erlang.mk: Rolling release, keep on rollin'</li>
+ <li>New project! Looking Glass</li>
+ </ul>
+ </section>
+
+ <section>
+ <h2>Looking Glass</h2>
+ <ul>
+ <li>Next-gen profiler for RabbitMQ</li>
+ <li><code>erl_tracer</code> NIF</li>
+ <li>LZ4 compression of trace files</li>
+ <li>Cachegrind output</li>
+ <li>Really, really fast</li>
+ <li><a href="https://github.com/rabbitmq/looking-glass">https://github.com/rabbitmq/looking-glass</a></li>
+ </ul>
+ </section>
+</section>
+
+<section>
+ <section>
+ <h2>Cowboy 2.1+</h2>
+ <ul>
+ <li>Features cut from 2.0 added back</li>
+ <li>More features (related to standards)</li>
+ <li>Improve interface with backward compatibility</li>
+ <li>Fix bugs, add tests, become a legend</li>
+ </ul>
+ </section>
+
+ <section>
+ <p>And eventually release Cowboy 3.0</p>
+ </section>
+</section>
+
+<section>
+ <h2>After Cowboy 2.0</h2>
+ <p>Finally build that REST framework</p>
+ <p>Make (the true) REST easily accessible</p>
+</section>
+
+<section>
+ <h2>Thank you in advance</h2>
+ <p><a href="https://ninenines.eu">ninenines.eu</a></p>
+</section>
+
+ </div>
+ </div>
+ <script src="js/reveal.js"></script>
+ <script>
+Reveal.initialize({
+// controls: false,
+ progress: false,
+ history: true
+});
+ </script>
+ </body>
+</html>