summaryrefslogtreecommitdiffstats
path: root/articles/ranch-ftp/index.html
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2017-10-03 13:39:41 +0200
committerLoïc Hoguin <[email protected]>2017-10-03 13:39:41 +0200
commitb5d4cb91f80c833795a2d87050c3674bb7aecdc5 (patch)
tree62bf0ad8326006fcd3407fcb7c34c844c0dc0874 /articles/ranch-ftp/index.html
parent1f8d51dd2692fc3978080419987bbe4d49a41a90 (diff)
downloadninenines.eu-b5d4cb91f80c833795a2d87050c3674bb7aecdc5.tar.gz
ninenines.eu-b5d4cb91f80c833795a2d87050c3674bb7aecdc5.tar.bz2
ninenines.eu-b5d4cb91f80c833795a2d87050c3674bb7aecdc5.zip
Update Hugo, docs
Diffstat (limited to 'articles/ranch-ftp/index.html')
-rw-r--r--articles/ranch-ftp/index.html524
1 files changed, 288 insertions, 236 deletions
diff --git a/articles/ranch-ftp/index.html b/articles/ranch-ftp/index.html
index 727d4b1a..241750f0 100644
--- a/articles/ranch-ftp/index.html
+++ b/articles/ranch-ftp/index.html
@@ -7,7 +7,7 @@
<meta name="description" content="">
<meta name="author" content="Loïc Hoguin based on a design from (Soft10) Pol Cámara">
- <meta name="generator" content="Hugo 0.17" />
+ <meta name="generator" content="Hugo 0.26" />
<title>Nine Nines: Build an FTP Server with Ranch in 30 Minutes</title>
@@ -74,216 +74,216 @@
</p>
</header>
-<div class="paragraph"><p>Last week I was speaking at the
-<a href="http://www.erlang-factory.com/conference/London2012/speakers/LoicHoguin">London Erlang Factory Lite</a>
-where I presented a live demonstration of building an FTP server using
-<a href="http://ninenines.eu/docs/en/ranch/HEAD/README">Ranch</a>.
-As there was no slide, you should use this article as a reference instead.</p></div>
-<div class="paragraph"><p>The goal of this article is to showcase how to use Ranch for writing
-a network protocol implementation, how Ranch gets out of the way to let
-you write the code that matters, and the common techniques used when
-writing servers.</p></div>
-<div class="paragraph"><p>Let&#8217;s start by creating an empty project. Create a new directory and
-then open a terminal into that directory. The first step is to add Ranch
-as a dependency. Create the <code>rebar.config</code> file and add the
-following 3 lines.</p></div>
-<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt>{<span style="color: #FF6600">deps</span>, [
- {<span style="color: #FF6600">ranch</span>, <span style="color: #FF0000">".*"</span>, {<span style="color: #FF6600">git</span>, <span style="color: #FF0000">"git://github.com/extend/ranch.git"</span>, <span style="color: #FF0000">"master"</span>}}
-]}<span style="color: #990000">.</span></tt></pre></div></div>
-<div class="paragraph"><p>This makes your application depend on the last Ranch version available
-on the <em>master</em> branch. This is fine for development, however when
-you start pushing your application to production you will want to revisit
-this file to hardcode the exact version you are using, to make sure you
-run the same version of dependencies in production.</p></div>
-<div class="paragraph"><p>You can now fetch the dependencies.</p></div>
-<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt>$ rebar get-deps
-<span style="color: #990000">==&gt;</span> ranch_ftp <span style="color: #990000">(</span>get-deps<span style="color: #990000">)</span>
-Pulling ranch from {git<span style="color: #990000">,</span><span style="color: #FF0000">"git://github.com/extend/ranch.git"</span><span style="color: #990000">,</span><span style="color: #FF0000">"master"</span>}
-Cloning into <span style="color: #FF0000">'ranch'</span><span style="color: #990000">...</span>
-<span style="color: #990000">==&gt;</span> ranch <span style="color: #990000">(</span>get-deps<span style="color: #990000">)</span></tt></pre></div></div>
-<div class="paragraph"><p>This will create a <em>deps/</em> folder containing Ranch.</p></div>
-<div class="paragraph"><p>We don&#8217;t actually need anything else to write the protocol code.
-We could make an application for it, but this isn&#8217;t the purpose of this
-article so let&#8217;s just move on to writing the protocol itself. Create
-the file <em>ranch_ftp_protocol.erl</em> and open it in your favorite
-editor.</p></div>
-<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt>$ vim ranch_ftp_protocol<span style="color: #990000">.</span>erl</tt></pre></div></div>
-<div class="paragraph"><p>Let&#8217;s start with a blank protocol module.</p></div>
-<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt><span style="font-weight: bold"><span style="color: #000080">-module</span></span>(<span style="color: #FF6600">ranch_ftp_protocol</span>)<span style="color: #990000">.</span>
-<span style="font-weight: bold"><span style="color: #000080">-export</span></span>([<span style="font-weight: bold"><span style="color: #000000">start_link</span></span><span style="color: #990000">/</span><span style="color: #993399">4</span>, <span style="font-weight: bold"><span style="color: #000000">init</span></span><span style="color: #990000">/</span><span style="color: #993399">3</span>])<span style="color: #990000">.</span>
-
-<span style="font-weight: bold"><span style="color: #000000">start_link</span></span>(<span style="color: #009900">ListenerPid</span>, <span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>, <span style="color: #009900">Opts</span>) <span style="color: #990000">-&gt;</span>
- <span style="color: #009900">Pid</span> <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #000080">spawn_link</span></span>(<span style="font-weight: bold"><span style="color: #000080">?MODULE</span></span>, <span style="color: #FF6600">init</span>, [<span style="color: #009900">ListenerPid</span>, <span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>]),
- {<span style="color: #FF6600">ok</span>, <span style="color: #009900">Pid</span>}<span style="color: #990000">.</span>
-
-<span style="font-weight: bold"><span style="color: #000000">init</span></span>(<span style="color: #009900">ListenerPid</span>, <span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>) <span style="color: #990000">-&gt;</span>
- <span style="font-weight: bold"><span style="color: #000000">io:format</span></span>(<span style="color: #FF0000">"Got a connection!~n"</span>),
- <span style="color: #FF6600">ok</span><span style="color: #990000">.</span></tt></pre></div></div>
-<div class="paragraph"><p>When Ranch receives a connection, it will call the &lt;code&gt;start_link/4&lt;/code&gt;
-function with the listener&#8217;s pid, socket, transport module to be used,
-and the options we define when starting the listener. We don&#8217;t need options
-for the purpose of this article, so we don&#8217;t pass them to the process we are
-creating.</p></div>
-<div class="paragraph"><p>Let&#8217;s open a shell and start a Ranch listener to begin accepting
-connections. We only need to call one function. You should probably open
-it in another terminal and keep it open for convenience. If you quit
-the shell you will have to repeat the commands to proceed.</p></div>
-<div class="paragraph"><p>Also note that you need to type <code>c(ranch_ftp_protocol).</code>
-to recompile and reload the code for the protocol. You do not need to
-restart any process however.</p></div>
-<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt>$ erl -pa ebin deps<span style="color: #990000">/*</span>/ebin
-Erlang R15B02 <span style="color: #990000">(</span>erts-<span style="color: #993399">5.9</span><span style="color: #990000">.</span><span style="color: #993399">2</span><span style="color: #990000">)</span> <span style="color: #990000">[</span><span style="font-weight: bold"><span style="color: #0000FF">source</span></span><span style="color: #990000">]</span> <span style="color: #990000">[</span><span style="color: #993399">64</span>-bit<span style="color: #990000">]</span> <span style="color: #990000">[</span>smp<span style="color: #990000">:</span><span style="color: #993399">4</span><span style="color: #990000">:</span><span style="color: #993399">4</span><span style="color: #990000">]</span> <span style="color: #990000">[</span>async-threads<span style="color: #990000">:</span><span style="color: #993399">0</span><span style="color: #990000">]</span> <span style="color: #990000">[</span>hipe<span style="color: #990000">]</span> <span style="color: #990000">[</span>kernel-poll<span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">false</span></span><span style="color: #990000">]</span>
-
-Eshell V5<span style="color: #990000">.</span><span style="color: #993399">9.2</span> <span style="color: #990000">(</span>abort with <span style="color: #990000">^</span>G<span style="color: #990000">)</span></tt></pre></div></div>
-<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt><span style="color: #993399">1</span><span style="color: #990000">&gt;</span> <span style="font-weight: bold"><span style="color: #000000">application:start</span></span>(<span style="color: #FF6600">ranch</span>)<span style="color: #990000">.</span>
-<span style="color: #FF6600">ok</span>
-<span style="color: #993399">2</span><span style="color: #990000">&gt;</span> <span style="font-weight: bold"><span style="color: #000000">ranch:start_listener</span></span>(<span style="color: #FF6600">my_ftp</span>, <span style="color: #993399">10</span>,
- <span style="color: #FF6600">ranch_tcp</span>, [{<span style="color: #FF6600">port</span>, <span style="color: #993399">2121</span>}],
- <span style="color: #FF6600">ranch_ftp_protocol</span>, [])<span style="color: #990000">.</span>
-{<span style="color: #FF6600">ok</span>,<span style="color: #990000">&lt;</span><span style="color: #993399">0.40</span><span style="color: #990000">.</span><span style="color: #993399">0</span><span style="color: #990000">&gt;</span>}</tt></pre></div></div>
-<div class="paragraph"><p>This starts a listener named <code>my_ftp</code> that runs your very own
-<code>ranch_ftp_protocol</code> over TCP, listening on port <code>2121</code>.
-The last argument is the options given to the protocol that we ignored
-earlier.</p></div>
-<div class="paragraph"><p>To try your code, you can use the following command. It should be able
-to connect, the server will print a message in the console, and then
-the client will print an error.</p></div>
-<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt>$ ftp localhost <span style="color: #993399">2121</span></tt></pre></div></div>
-<div class="paragraph"><p>Let&#8217;s move on to actually writing the protocol.</p></div>
-<div class="paragraph"><p>Once you have created the new process and returned the pid, Ranch will
-give ownership of the socket to you. This requires a synchronization
-step though.</p></div>
-<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt><span style="font-weight: bold"><span style="color: #000000">init</span></span>(<span style="color: #009900">ListenerPid</span>, <span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>) <span style="color: #990000">-&gt;</span>
- <span style="color: #0000FF">ok</span> <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #000000">ranch:accept_ack</span></span>(<span style="color: #009900">ListenerPid</span>),
- <span style="color: #FF6600">ok</span><span style="color: #990000">.</span></tt></pre></div></div>
-<div class="paragraph"><p>Now that you acknowledged the new connection, you can use it safely.</p></div>
-<div class="paragraph"><p>When an FTP server accepts a connection, it starts by sending a
-welcome message which can be one or more lines starting with the
-code <code>200</code>. Then the server will wait for the client
-to authenticate the user, and if the authentication went successfully,
-which it will always do for the purpose of this article, it will reply
-with a <code>230</code> code.</p></div>
-<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt><span style="font-weight: bold"><span style="color: #000000">init</span></span>(<span style="color: #009900">ListenerPid</span>, <span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>) <span style="color: #990000">-&gt;</span>
- <span style="color: #0000FF">ok</span> <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #000000">ranch:accept_ack</span></span>(<span style="color: #009900">ListenerPid</span>),
- <span style="color: #009900">Transport</span><span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #000000">send</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #990000">&lt;&lt;</span><span style="color: #FF0000">"200 My cool FTP server welcomes you!\r\n"</span><span style="color: #990000">&gt;&gt;</span>),
- {<span style="color: #FF6600">ok</span>, <span style="color: #009900">Data</span>} <span style="color: #990000">=</span> <span style="color: #009900">Transport</span><span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #000000">recv</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #993399">0</span>, <span style="color: #993399">30000</span>),
- <span style="font-weight: bold"><span style="color: #000000">auth</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>, <span style="color: #009900">Data</span>)<span style="color: #990000">.</span>
-
-<span style="font-weight: bold"><span style="color: #000000">auth</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>, <span style="color: #990000">&lt;&lt;</span><span style="color: #FF0000">"USER "</span>, <span style="color: #009900">Rest</span><span style="color: #990000">/</span><span style="color: #FF6600">bits</span><span style="color: #990000">&gt;&gt;</span>) <span style="color: #990000">-&gt;</span>
- <span style="font-weight: bold"><span style="color: #000000">io:format</span></span>(<span style="color: #FF0000">"User authenticated! ~p~n"</span>, [<span style="color: #009900">Rest</span>]),
- <span style="color: #009900">Transport</span><span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #000000">send</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #990000">&lt;&lt;</span><span style="color: #FF0000">"230 Auth OK\r\n"</span><span style="color: #990000">&gt;&gt;</span>),
- <span style="color: #FF6600">ok</span><span style="color: #990000">.</span></tt></pre></div></div>
-<div class="paragraph"><p>As you can see we don&#8217;t need complex parsing code. We can simply
-match on the binary in the argument!</p></div>
-<div class="paragraph"><p>Next we need to loop receiving data commands and optionally
-execute them, if we want our server to become useful.</p></div>
-<div class="paragraph"><p>We will replace the &lt;code&gt;ok.&lt;/code&gt; line with the call to
-the following function. The new function is recursive, each call
-receiving data from the socket and sending a response. For now
-we will send an error response for all commands the client sends.</p></div>
-<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt><span style="font-weight: bold"><span style="color: #000000">loop</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>) <span style="color: #990000">-&gt;</span>
- <span style="font-weight: bold"><span style="color: #0000FF">case</span></span> <span style="color: #009900">Transport</span><span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #000000">recv</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #993399">0</span>, <span style="color: #993399">30000</span>) <span style="font-weight: bold"><span style="color: #0000FF">of</span></span>
- {<span style="color: #FF6600">ok</span>, <span style="color: #009900">Data</span>} <span style="color: #990000">-&gt;</span>
- <span style="font-weight: bold"><span style="color: #000000">handle</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>, <span style="color: #009900">Data</span>),
- <span style="font-weight: bold"><span style="color: #000000">loop</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>);
- {<span style="color: #FF6600">error</span>, <span style="color: #990000">_</span>} <span style="color: #990000">-&gt;</span>
- <span style="font-weight: bold"><span style="color: #000000">io:format</span></span>(<span style="color: #FF0000">"The client disconnected~n"</span>)
- <span style="font-weight: bold"><span style="color: #0000FF">end</span></span><span style="color: #990000">.</span>
-
-<span style="font-weight: bold"><span style="color: #000000">handle</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>, <span style="color: #009900">Data</span>) <span style="color: #990000">-&gt;</span>
- <span style="font-weight: bold"><span style="color: #000000">io:format</span></span>(<span style="color: #FF0000">"Command received ~p~n"</span>, [<span style="color: #009900">Data</span>]),
- <span style="color: #009900">Transport</span><span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #000000">send</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #990000">&lt;&lt;</span><span style="color: #FF0000">"500 Bad command\r\n"</span><span style="color: #990000">&gt;&gt;</span>)<span style="color: #990000">.</span></tt></pre></div></div>
-<div class="paragraph"><p>With this we are almost ready to start implementing commands.
-But with code like this we might have errors if the client doesn&#8217;t
-send just one command per packet, or if the packets arrive too fast,
-or if a command is split over multiple packets.</p></div>
-<div class="paragraph"><p>To solve this, we need to use a buffer. Each time we receive data,
-we will append to the buffer, and then check if we have received a
-command fully before running it. The code could look similar to the
-following.</p></div>
-<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt><span style="font-weight: bold"><span style="color: #000000">loop</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>, <span style="color: #009900">Buffer</span>) <span style="color: #990000">-&gt;</span>
- <span style="font-weight: bold"><span style="color: #0000FF">case</span></span> <span style="color: #009900">Transport</span><span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #000000">recv</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #993399">0</span>, <span style="color: #993399">30000</span>) <span style="font-weight: bold"><span style="color: #0000FF">of</span></span>
- {<span style="color: #FF6600">ok</span>, <span style="color: #009900">Data</span>} <span style="color: #990000">-&gt;</span>
- <span style="color: #009900">Buffer2</span> <span style="color: #990000">=</span> <span style="color: #990000">&lt;&lt;</span> <span style="color: #009900">Buffer</span><span style="color: #990000">/</span><span style="font-weight: bold"><span style="color: #000080">binary</span></span>, <span style="color: #009900">Data</span><span style="color: #990000">/</span><span style="font-weight: bold"><span style="color: #000080">binary</span></span> <span style="color: #990000">&gt;&gt;</span>,
- {<span style="color: #009900">Commands</span>, <span style="color: #009900">Rest</span>} <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #000000">split</span></span>(<span style="color: #009900">Buffer2</span>),
- [<span style="font-weight: bold"><span style="color: #000000">handle</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>, <span style="color: #009900">C</span>) || <span style="color: #009900">C</span> <span style="color: #990000">&lt;-</span> <span style="color: #009900">Commands</span>],
- <span style="font-weight: bold"><span style="color: #000000">loop</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>, <span style="color: #009900">Rest</span>);
- {<span style="color: #FF6600">error</span>, <span style="color: #990000">_</span>} <span style="color: #990000">-&gt;</span>
- <span style="font-weight: bold"><span style="color: #000000">io:format</span></span>(<span style="color: #FF0000">"The client disconnected~n"</span>)
- <span style="font-weight: bold"><span style="color: #0000FF">end</span></span><span style="color: #990000">.</span></tt></pre></div></div>
-<div class="paragraph"><p>The implementation of <code>split/1</code> is left as an exercice
-to the reader. You will also probably want to handle the <code>QUIT</code>
-command, which must stop any processing and close the connection.</p></div>
-<div class="paragraph"><p>The attentive reader will also take note that in the case of text-
-based protocols where commands are separated by line breaks, you can
-set an option using <code>Transport:setopts/2</code> and have all the
-buffering done for you for free by Erlang itself.</p></div>
-<div class="paragraph"><p>As you can surely notice by now, Ranch allows us to build network
-applications by getting out of our way entirely past the initial setup.
-It lets you use the power of binary pattern matching to write text and
-binary protocol implementations in just a few lines of code.</p></div>
-<div class="ulist"><ul>
-<li>
-<p>
-<a href="http://www.erlang-factory.com/conference/London2012/speakers/LoicHoguin">Watch the talk</a>
-</p>
-</li>
-</ul></div>
+<div class="paragraph"><p>Last week I was speaking at the
+<a href="http://www.erlang-factory.com/conference/London2012/speakers/LoicHoguin">London Erlang Factory Lite</a>
+where I presented a live demonstration of building an FTP server using
+<a href="http://ninenines.eu/docs/en/ranch/HEAD/README">Ranch</a>.
+As there was no slide, you should use this article as a reference instead.</p></div>
+<div class="paragraph"><p>The goal of this article is to showcase how to use Ranch for writing
+a network protocol implementation, how Ranch gets out of the way to let
+you write the code that matters, and the common techniques used when
+writing servers.</p></div>
+<div class="paragraph"><p>Let&#8217;s start by creating an empty project. Create a new directory and
+then open a terminal into that directory. The first step is to add Ranch
+as a dependency. Create the <code>rebar.config</code> file and add the
+following 3 lines.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>{<span style="color: #FF6600">deps</span>, [
+ {<span style="color: #FF6600">ranch</span>, <span style="color: #FF0000">".*"</span>, {<span style="color: #FF6600">git</span>, <span style="color: #FF0000">"git://github.com/extend/ranch.git"</span>, <span style="color: #FF0000">"master"</span>}}
+]}<span style="color: #990000">.</span></tt></pre></div></div>
+<div class="paragraph"><p>This makes your application depend on the last Ranch version available
+on the <em>master</em> branch. This is fine for development, however when
+you start pushing your application to production you will want to revisit
+this file to hardcode the exact version you are using, to make sure you
+run the same version of dependencies in production.</p></div>
+<div class="paragraph"><p>You can now fetch the dependencies.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ rebar get-deps
+<span style="color: #990000">==&gt;</span> ranch_ftp <span style="color: #990000">(</span>get-deps<span style="color: #990000">)</span>
+Pulling ranch from {git<span style="color: #990000">,</span><span style="color: #FF0000">"git://github.com/extend/ranch.git"</span><span style="color: #990000">,</span><span style="color: #FF0000">"master"</span>}
+Cloning into <span style="color: #FF0000">'ranch'</span><span style="color: #990000">...</span>
+<span style="color: #990000">==&gt;</span> ranch <span style="color: #990000">(</span>get-deps<span style="color: #990000">)</span></tt></pre></div></div>
+<div class="paragraph"><p>This will create a <em>deps/</em> folder containing Ranch.</p></div>
+<div class="paragraph"><p>We don&#8217;t actually need anything else to write the protocol code.
+We could make an application for it, but this isn&#8217;t the purpose of this
+article so let&#8217;s just move on to writing the protocol itself. Create
+the file <em>ranch_ftp_protocol.erl</em> and open it in your favorite
+editor.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ vim ranch_ftp_protocol<span style="color: #990000">.</span>erl</tt></pre></div></div>
+<div class="paragraph"><p>Let&#8217;s start with a blank protocol module.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">-module</span></span>(<span style="color: #FF6600">ranch_ftp_protocol</span>)<span style="color: #990000">.</span>
+<span style="font-weight: bold"><span style="color: #000080">-export</span></span>([<span style="font-weight: bold"><span style="color: #000000">start_link</span></span><span style="color: #990000">/</span><span style="color: #993399">4</span>, <span style="font-weight: bold"><span style="color: #000000">init</span></span><span style="color: #990000">/</span><span style="color: #993399">3</span>])<span style="color: #990000">.</span>
+
+<span style="font-weight: bold"><span style="color: #000000">start_link</span></span>(<span style="color: #009900">ListenerPid</span>, <span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>, <span style="color: #009900">Opts</span>) <span style="color: #990000">-&gt;</span>
+ <span style="color: #009900">Pid</span> <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #000080">spawn_link</span></span>(<span style="font-weight: bold"><span style="color: #000080">?MODULE</span></span>, <span style="color: #FF6600">init</span>, [<span style="color: #009900">ListenerPid</span>, <span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>]),
+ {<span style="color: #FF6600">ok</span>, <span style="color: #009900">Pid</span>}<span style="color: #990000">.</span>
+
+<span style="font-weight: bold"><span style="color: #000000">init</span></span>(<span style="color: #009900">ListenerPid</span>, <span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>) <span style="color: #990000">-&gt;</span>
+ <span style="font-weight: bold"><span style="color: #000000">io:format</span></span>(<span style="color: #FF0000">"Got a connection!~n"</span>),
+ <span style="color: #FF6600">ok</span><span style="color: #990000">.</span></tt></pre></div></div>
+<div class="paragraph"><p>When Ranch receives a connection, it will call the &lt;code&gt;start_link/4&lt;/code&gt;
+function with the listener&#8217;s pid, socket, transport module to be used,
+and the options we define when starting the listener. We don&#8217;t need options
+for the purpose of this article, so we don&#8217;t pass them to the process we are
+creating.</p></div>
+<div class="paragraph"><p>Let&#8217;s open a shell and start a Ranch listener to begin accepting
+connections. We only need to call one function. You should probably open
+it in another terminal and keep it open for convenience. If you quit
+the shell you will have to repeat the commands to proceed.</p></div>
+<div class="paragraph"><p>Also note that you need to type <code>c(ranch_ftp_protocol).</code>
+to recompile and reload the code for the protocol. You do not need to
+restart any process however.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ erl -pa ebin deps<span style="color: #990000">/*</span>/ebin
+Erlang R15B02 <span style="color: #990000">(</span>erts-<span style="color: #993399">5.9</span><span style="color: #990000">.</span><span style="color: #993399">2</span><span style="color: #990000">)</span> <span style="color: #990000">[</span><span style="font-weight: bold"><span style="color: #0000FF">source</span></span><span style="color: #990000">]</span> <span style="color: #990000">[</span><span style="color: #993399">64</span>-bit<span style="color: #990000">]</span> <span style="color: #990000">[</span>smp<span style="color: #990000">:</span><span style="color: #993399">4</span><span style="color: #990000">:</span><span style="color: #993399">4</span><span style="color: #990000">]</span> <span style="color: #990000">[</span>async-threads<span style="color: #990000">:</span><span style="color: #993399">0</span><span style="color: #990000">]</span> <span style="color: #990000">[</span>hipe<span style="color: #990000">]</span> <span style="color: #990000">[</span>kernel-poll<span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">false</span></span><span style="color: #990000">]</span>
+
+Eshell V5<span style="color: #990000">.</span><span style="color: #993399">9.2</span> <span style="color: #990000">(</span>abort with <span style="color: #990000">^</span>G<span style="color: #990000">)</span></tt></pre></div></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #993399">1</span><span style="color: #990000">&gt;</span> <span style="font-weight: bold"><span style="color: #000000">application:start</span></span>(<span style="color: #FF6600">ranch</span>)<span style="color: #990000">.</span>
+<span style="color: #FF6600">ok</span>
+<span style="color: #993399">2</span><span style="color: #990000">&gt;</span> <span style="font-weight: bold"><span style="color: #000000">ranch:start_listener</span></span>(<span style="color: #FF6600">my_ftp</span>, <span style="color: #993399">10</span>,
+ <span style="color: #FF6600">ranch_tcp</span>, [{<span style="color: #FF6600">port</span>, <span style="color: #993399">2121</span>}],
+ <span style="color: #FF6600">ranch_ftp_protocol</span>, [])<span style="color: #990000">.</span>
+{<span style="color: #FF6600">ok</span>,<span style="color: #990000">&lt;</span><span style="color: #993399">0.40</span><span style="color: #990000">.</span><span style="color: #993399">0</span><span style="color: #990000">&gt;</span>}</tt></pre></div></div>
+<div class="paragraph"><p>This starts a listener named <code>my_ftp</code> that runs your very own
+<code>ranch_ftp_protocol</code> over TCP, listening on port <code>2121</code>.
+The last argument is the options given to the protocol that we ignored
+earlier.</p></div>
+<div class="paragraph"><p>To try your code, you can use the following command. It should be able
+to connect, the server will print a message in the console, and then
+the client will print an error.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ ftp localhost <span style="color: #993399">2121</span></tt></pre></div></div>
+<div class="paragraph"><p>Let&#8217;s move on to actually writing the protocol.</p></div>
+<div class="paragraph"><p>Once you have created the new process and returned the pid, Ranch will
+give ownership of the socket to you. This requires a synchronization
+step though.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000000">init</span></span>(<span style="color: #009900">ListenerPid</span>, <span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>) <span style="color: #990000">-&gt;</span>
+ <span style="color: #0000FF">ok</span> <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #000000">ranch:accept_ack</span></span>(<span style="color: #009900">ListenerPid</span>),
+ <span style="color: #FF6600">ok</span><span style="color: #990000">.</span></tt></pre></div></div>
+<div class="paragraph"><p>Now that you acknowledged the new connection, you can use it safely.</p></div>
+<div class="paragraph"><p>When an FTP server accepts a connection, it starts by sending a
+welcome message which can be one or more lines starting with the
+code <code>200</code>. Then the server will wait for the client
+to authenticate the user, and if the authentication went successfully,
+which it will always do for the purpose of this article, it will reply
+with a <code>230</code> code.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000000">init</span></span>(<span style="color: #009900">ListenerPid</span>, <span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>) <span style="color: #990000">-&gt;</span>
+ <span style="color: #0000FF">ok</span> <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #000000">ranch:accept_ack</span></span>(<span style="color: #009900">ListenerPid</span>),
+ <span style="color: #009900">Transport</span><span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #000000">send</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #990000">&lt;&lt;</span><span style="color: #FF0000">"200 My cool FTP server welcomes you!\r\n"</span><span style="color: #990000">&gt;&gt;</span>),
+ {<span style="color: #FF6600">ok</span>, <span style="color: #009900">Data</span>} <span style="color: #990000">=</span> <span style="color: #009900">Transport</span><span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #000000">recv</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #993399">0</span>, <span style="color: #993399">30000</span>),
+ <span style="font-weight: bold"><span style="color: #000000">auth</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>, <span style="color: #009900">Data</span>)<span style="color: #990000">.</span>
+
+<span style="font-weight: bold"><span style="color: #000000">auth</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>, <span style="color: #990000">&lt;&lt;</span><span style="color: #FF0000">"USER "</span>, <span style="color: #009900">Rest</span><span style="color: #990000">/</span><span style="color: #FF6600">bits</span><span style="color: #990000">&gt;&gt;</span>) <span style="color: #990000">-&gt;</span>
+ <span style="font-weight: bold"><span style="color: #000000">io:format</span></span>(<span style="color: #FF0000">"User authenticated! ~p~n"</span>, [<span style="color: #009900">Rest</span>]),
+ <span style="color: #009900">Transport</span><span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #000000">send</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #990000">&lt;&lt;</span><span style="color: #FF0000">"230 Auth OK\r\n"</span><span style="color: #990000">&gt;&gt;</span>),
+ <span style="color: #FF6600">ok</span><span style="color: #990000">.</span></tt></pre></div></div>
+<div class="paragraph"><p>As you can see we don&#8217;t need complex parsing code. We can simply
+match on the binary in the argument!</p></div>
+<div class="paragraph"><p>Next we need to loop receiving data commands and optionally
+execute them, if we want our server to become useful.</p></div>
+<div class="paragraph"><p>We will replace the &lt;code&gt;ok.&lt;/code&gt; line with the call to
+the following function. The new function is recursive, each call
+receiving data from the socket and sending a response. For now
+we will send an error response for all commands the client sends.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000000">loop</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>) <span style="color: #990000">-&gt;</span>
+ <span style="font-weight: bold"><span style="color: #0000FF">case</span></span> <span style="color: #009900">Transport</span><span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #000000">recv</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #993399">0</span>, <span style="color: #993399">30000</span>) <span style="font-weight: bold"><span style="color: #0000FF">of</span></span>
+ {<span style="color: #FF6600">ok</span>, <span style="color: #009900">Data</span>} <span style="color: #990000">-&gt;</span>
+ <span style="font-weight: bold"><span style="color: #000000">handle</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>, <span style="color: #009900">Data</span>),
+ <span style="font-weight: bold"><span style="color: #000000">loop</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>);
+ {<span style="color: #FF6600">error</span>, <span style="color: #990000">_</span>} <span style="color: #990000">-&gt;</span>
+ <span style="font-weight: bold"><span style="color: #000000">io:format</span></span>(<span style="color: #FF0000">"The client disconnected~n"</span>)
+ <span style="font-weight: bold"><span style="color: #0000FF">end</span></span><span style="color: #990000">.</span>
+
+<span style="font-weight: bold"><span style="color: #000000">handle</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>, <span style="color: #009900">Data</span>) <span style="color: #990000">-&gt;</span>
+ <span style="font-weight: bold"><span style="color: #000000">io:format</span></span>(<span style="color: #FF0000">"Command received ~p~n"</span>, [<span style="color: #009900">Data</span>]),
+ <span style="color: #009900">Transport</span><span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #000000">send</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #990000">&lt;&lt;</span><span style="color: #FF0000">"500 Bad command\r\n"</span><span style="color: #990000">&gt;&gt;</span>)<span style="color: #990000">.</span></tt></pre></div></div>
+<div class="paragraph"><p>With this we are almost ready to start implementing commands.
+But with code like this we might have errors if the client doesn&#8217;t
+send just one command per packet, or if the packets arrive too fast,
+or if a command is split over multiple packets.</p></div>
+<div class="paragraph"><p>To solve this, we need to use a buffer. Each time we receive data,
+we will append to the buffer, and then check if we have received a
+command fully before running it. The code could look similar to the
+following.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #000000">loop</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>, <span style="color: #009900">Buffer</span>) <span style="color: #990000">-&gt;</span>
+ <span style="font-weight: bold"><span style="color: #0000FF">case</span></span> <span style="color: #009900">Transport</span><span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #000000">recv</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #993399">0</span>, <span style="color: #993399">30000</span>) <span style="font-weight: bold"><span style="color: #0000FF">of</span></span>
+ {<span style="color: #FF6600">ok</span>, <span style="color: #009900">Data</span>} <span style="color: #990000">-&gt;</span>
+ <span style="color: #009900">Buffer2</span> <span style="color: #990000">=</span> <span style="color: #990000">&lt;&lt;</span> <span style="color: #009900">Buffer</span><span style="color: #990000">/</span><span style="font-weight: bold"><span style="color: #000080">binary</span></span>, <span style="color: #009900">Data</span><span style="color: #990000">/</span><span style="font-weight: bold"><span style="color: #000080">binary</span></span> <span style="color: #990000">&gt;&gt;</span>,
+ {<span style="color: #009900">Commands</span>, <span style="color: #009900">Rest</span>} <span style="color: #990000">=</span> <span style="font-weight: bold"><span style="color: #000000">split</span></span>(<span style="color: #009900">Buffer2</span>),
+ [<span style="font-weight: bold"><span style="color: #000000">handle</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>, <span style="color: #009900">C</span>) || <span style="color: #009900">C</span> <span style="color: #990000">&lt;-</span> <span style="color: #009900">Commands</span>],
+ <span style="font-weight: bold"><span style="color: #000000">loop</span></span>(<span style="color: #009900">Socket</span>, <span style="color: #009900">Transport</span>, <span style="color: #009900">Rest</span>);
+ {<span style="color: #FF6600">error</span>, <span style="color: #990000">_</span>} <span style="color: #990000">-&gt;</span>
+ <span style="font-weight: bold"><span style="color: #000000">io:format</span></span>(<span style="color: #FF0000">"The client disconnected~n"</span>)
+ <span style="font-weight: bold"><span style="color: #0000FF">end</span></span><span style="color: #990000">.</span></tt></pre></div></div>
+<div class="paragraph"><p>The implementation of <code>split/1</code> is left as an exercice
+to the reader. You will also probably want to handle the <code>QUIT</code>
+command, which must stop any processing and close the connection.</p></div>
+<div class="paragraph"><p>The attentive reader will also take note that in the case of text-
+based protocols where commands are separated by line breaks, you can
+set an option using <code>Transport:setopts/2</code> and have all the
+buffering done for you for free by Erlang itself.</p></div>
+<div class="paragraph"><p>As you can surely notice by now, Ranch allows us to build network
+applications by getting out of our way entirely past the initial setup.
+It lets you use the power of binary pattern matching to write text and
+binary protocol implementations in just a few lines of code.</p></div>
+<div class="ulist"><ul>
+<li>
+<p>
+<a href="http://www.erlang-factory.com/conference/London2012/speakers/LoicHoguin">Watch the talk</a>
+</p>
+</li>
+</ul></div>
</article>
</div>
@@ -292,55 +292,107 @@ binary protocol implementations in just a few lines of code.</p></div>
<h3>More articles</h3>
<ul id="articles-nav" class="extra_margin">
- <li><a href="https://ninenines.eu/articles/cowboy-2.0.0-rc.2/">Cowboy 2.0 release candidate 2</a></li>
+
+ <li><a href="https://ninenines.eu/articles/cowboy-2.0.0-rc.2/">Cowboy 2.0 release candidate 2</a></li>
+
+
+
+ <li><a href="https://ninenines.eu/articles/cowboy-2.0.0-rc.1/">Cowboy 2.0 release candidate 1</a></li>
+
- <li><a href="https://ninenines.eu/articles/cowboy-2.0.0-rc.1/">Cowboy 2.0 release candidate 1</a></li>
+
+ <li><a href="https://ninenines.eu/articles/the-elephant-in-the-room/">The elephant in the room</a></li>
+
- <li><a href="https://ninenines.eu/articles/the-elephant-in-the-room/">The elephant in the room</a></li>
+
+ <li><a href="https://ninenines.eu/articles/dont-let-it-crash/">Don&#39;t let it crash</a></li>
+
- <li><a href="https://ninenines.eu/articles/dont-let-it-crash/">Don&#39;t let it crash</a></li>
+
+ <li><a href="https://ninenines.eu/articles/cowboy-2.0.0-pre.4/">Cowboy 2.0 pre-release 4</a></li>
+
- <li><a href="https://ninenines.eu/articles/cowboy-2.0.0-pre.4/">Cowboy 2.0 pre-release 4</a></li>
+
+ <li><a href="https://ninenines.eu/articles/ranch-1.3/">Ranch 1.3</a></li>
+
- <li><a href="https://ninenines.eu/articles/ranch-1.3/">Ranch 1.3</a></li>
+
+ <li><a href="https://ninenines.eu/articles/ml-archives/">Mailing list archived</a></li>
+
- <li><a href="https://ninenines.eu/articles/ml-archives/">Mailing list archived</a></li>
+
+ <li><a href="https://ninenines.eu/articles/website-update/">Website update</a></li>
+
- <li><a href="https://ninenines.eu/articles/website-update/">Website update</a></li>
+
+ <li><a href="https://ninenines.eu/articles/erlanger-playbook-september-2015-update/">The Erlanger Playbook September 2015 Update</a></li>
+
- <li><a href="https://ninenines.eu/articles/erlanger-playbook-september-2015-update/">The Erlanger Playbook September 2015 Update</a></li>
+
+ <li><a href="https://ninenines.eu/articles/erlanger-playbook/">The Erlanger Playbook</a></li>
+
- <li><a href="https://ninenines.eu/articles/erlanger-playbook/">The Erlanger Playbook</a></li>
+
+ <li><a href="https://ninenines.eu/articles/erlang-validate-utf8/">Validating UTF-8 binaries with Erlang</a></li>
+
- <li><a href="https://ninenines.eu/articles/erlang-validate-utf8/">Validating UTF-8 binaries with Erlang</a></li>
+
+ <li><a href="https://ninenines.eu/articles/on-open-source/">On open source</a></li>
+
- <li><a href="https://ninenines.eu/articles/on-open-source/">On open source</a></li>
+
+ <li><a href="https://ninenines.eu/articles/the-story-so-far/">The story so far</a></li>
+
- <li><a href="https://ninenines.eu/articles/the-story-so-far/">The story so far</a></li>
+
+ <li><a href="https://ninenines.eu/articles/cowboy2-qs/">Cowboy 2.0 and query strings</a></li>
+
- <li><a href="https://ninenines.eu/articles/cowboy2-qs/">Cowboy 2.0 and query strings</a></li>
+
+ <li><a href="https://ninenines.eu/articles/january-2014-status/">January 2014 status</a></li>
+
- <li><a href="https://ninenines.eu/articles/january-2014-status/">January 2014 status</a></li>
+
+ <li><a href="https://ninenines.eu/articles/farwest-funded/">Farwest got funded!</a></li>
+
- <li><a href="https://ninenines.eu/articles/farwest-funded/">Farwest got funded!</a></li>
+
+ <li><a href="https://ninenines.eu/articles/erlang.mk-and-relx/">Build Erlang releases with Erlang.mk and Relx</a></li>
+
- <li><a href="https://ninenines.eu/articles/erlang.mk-and-relx/">Build Erlang releases with Erlang.mk and Relx</a></li>
+
+ <li><a href="https://ninenines.eu/articles/xerl-0.5-intermediate-module/">Xerl: intermediate module</a></li>
+
- <li><a href="https://ninenines.eu/articles/xerl-0.5-intermediate-module/">Xerl: intermediate module</a></li>
+
+ <li><a href="https://ninenines.eu/articles/xerl-0.4-expression-separator/">Xerl: expression separator</a></li>
+
- <li><a href="https://ninenines.eu/articles/xerl-0.4-expression-separator/">Xerl: expression separator</a></li>
+
+ <li><a href="https://ninenines.eu/articles/erlang-scalability/">Erlang Scalability</a></li>
+
- <li><a href="https://ninenines.eu/articles/erlang-scalability/">Erlang Scalability</a></li>
+
+ <li><a href="https://ninenines.eu/articles/xerl-0.3-atomic-expressions/">Xerl: atomic expressions</a></li>
+
- <li><a href="https://ninenines.eu/articles/xerl-0.3-atomic-expressions/">Xerl: atomic expressions</a></li>
+
+ <li><a href="https://ninenines.eu/articles/xerl-0.2-two-modules/">Xerl: two modules</a></li>
+
- <li><a href="https://ninenines.eu/articles/xerl-0.2-two-modules/">Xerl: two modules</a></li>
+
+ <li><a href="https://ninenines.eu/articles/xerl-0.1-empty-modules/">Xerl: empty modules</a></li>
+
- <li><a href="https://ninenines.eu/articles/xerl-0.1-empty-modules/">Xerl: empty modules</a></li>
+
+ <li><a href="https://ninenines.eu/articles/ranch-ftp/">Build an FTP Server with Ranch in 30 Minutes</a></li>
+
- <li><a href="https://ninenines.eu/articles/ranch-ftp/">Build an FTP Server with Ranch in 30 Minutes</a></li>
+
+ <li><a href="https://ninenines.eu/articles/tictactoe/">Erlang Tic Tac Toe</a></li>
+
- <li><a href="https://ninenines.eu/articles/tictactoe/">Erlang Tic Tac Toe</a></li>
+
</ul>