<p><div><div><div><div data-show-faces="false" data-height="21" data-width="100" data-layout="button_count" data-send="true" data-href="https://www.r-bloggers.com/introducing-routr-routing-of-http-and-websocket-in-r/"></div></div><div></div></div><div></div></div>
<div>
(This article was first published on <strong><a href="http://www.data-imaginist.com/2017/Introducing-routr/"> Data Imaginist - R posts</a></strong>, and kindly contributed to <a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers)</a> </div>
<p><img align="right" src="https://i1.wp.com/www.data-imaginist.com/assets/images/routr_logo_small.png?w=456" data-recalc-dims="1"></p>
<p><code>routr</code> is now available on CRAN, and I couldn’t be happier. It’s release marks<br>
the completion of an idea that stretches back longer than my attempts to bring<br>
network visualization and <code>ggplot2</code> together (<a rel="nofollow" href="http://www.data-imaginist.com/2016/Becoming-the-intern/" target="_blank">see this post for ref</a>).<br>
While my PhD was still concerned with proteomics a began developing GUI’s based<br>
on <code>shiny</code> for managing different parts of the proteomics workflow. I soon came<br>
to realize that I was spending an inordinate amount of time battling <code>shiny</code><br>
itself because I wanted more than it was meant for. Thus began my idea of<br>
creating an expressive and powerful web server framework for R in the veins of<br>
express.js and the likes that could be made to do anything. The idea lingered in<br>
my head for a long time and went through several iterations until I finally<br>
released <code>fiery</code> in the late summer of 2016. <code>fiery</code> was never meant to stand<br>
alone though and I boldly proclaimed that <code>routr</code> would come next. That didn’t<br>
seem to happen. I spend most of the following year developing tools for<br>
visualization and network analysis while having guilty consciousness about the<br>
project I’d put on hold. Fortunately I’ve been able to put in some time for<br>
taking up development for the <code>fiery</code> ecosystem once again, so without further<br>
ado…</p>
<h2>routr</h2>
<p>While I spend some time in the introduction to talk about the whole development<br>
path of <code>fiery</code>, I would like to start here with saying that <code>routr</code> is a server<br>
agnostic tool. Sure, I’ve build it for use with <code>fiery</code> but I’ve been very<br>
deliberate in making it completely independent of it, except for the code that<br>
is involved in the <code>fiery</code> plugin functionality. So, you’re completely free to<br>
use <code>routr</code> with whatever server framework you wish (e.g. hook it directly to<br>
an <code>httpuv</code> instance). But how does it work? read on…</p>
<h3>The design</h3>
<p><code>routr</code> is basically build up of two different concepts: <em>routes</em> and<br>
<em>route stacks</em>. Routes are a collection of handlers attached to specific HTTP<br>
request methods (e.g. GET, POST, PUT) and paths. When a request lands at a route<br>
one of the handlers is chosen and called, based on the nature of the request. A<br>
route stack is a collection of routes. When a request lands at a route stack it<br>
will pass it through all the routes it contains sequentially, potentially<br>
stopping if one of the handlers signals it. In the following these two concepts<br>
will be discussed in detail.</p>
<h3>Routes</h3>
<p>In its essence a router is a decision mechanism for redirection HTTP requests<br>
into the correct handler function based on the request URL. It makes sure that<br>
e.g. requests for <code>http://example.com/info</code> ends up in a different handler than<br>
<code>http://example.com/users/thomasp85</code>. This functionality is encapsulated in the<br>
<code>Route</code> class. The basic use is illustrated below:</p>
<figure>
<pre><code data-lang="r"><span>library</span><span>(</span><span>routr</span><span>)</span><span>
</span><span>route</span><span> </span><span>&lt;-</span><span> </span><span>Route</span><span>$</span><span>new</span><span>()</span><span>
</span><span>route</span><span>$</span><span>add_handler</span><span>(</span><span>'get'</span><span>,</span><span> </span><span>'/info'</span><span>,</span><span> </span><span>function</span><span>(</span><span>request</span><span>,</span><span> </span><span>response</span><span>,</span><span> </span><span>keys</span><span>,</span><span> </span><span>...</span><span>)</span><span> </span><span>{</span><span>
</span><span>response</span><span>$</span><span>status</span><span> </span><span>&lt;-</span><span> </span><span>200L</span><span>
</span><span>response</span><span>$</span><span>body</span><span> </span><span>&lt;-</span><span> </span><span>list</span><span>(</span><span>h</span><span>1</span><span> </span><span>=</span><span> </span><span>'This is a test server'</span><span>)</span><span>
</span><span>TRUE</span><span>
</span><span>})</span><span>
</span><span>route</span><span>$</span><span>add_handler</span><span>(</span><span>'get'</span><span>,</span><span> </span><span>'/users/thomasp85'</span><span>,</span><span> </span><span>function</span><span>(</span><span>request</span><span>,</span><span> </span><span>response</span><span>,</span><span> </span><span>keys</span><span>,</span><span> </span><span>...</span><span>)</span><span> </span><span>{</span><span>
</span><span>response</span><span>$</span><span>status</span><span> </span><span>&lt;-</span><span> </span><span>200L</span><span>
</span><span>response</span><span>$</span><span>body</span><span> </span><span>&lt;-</span><span> </span><span>list</span><span>(</span><span>h</span><span>1</span><span> </span><span>=</span><span> </span><span>'This is the user information for thomasp85'</span><span>)</span><span>
</span><span>TRUE</span><span>
</span><span>})</span><span>
</span><span>route</span></code></pre>
</figure>
<figure>
<pre><code data-lang="text">## A route with 2 handlers
## get: /users/thomasp85
##    : /info</code></pre>
</figure>
<p>Let’s walk through what happened here. First we created a new <code>Route</code> object and<br>
then we added two handlers to it, using the eponymous <code>add_handler()</code> method.<br>
Both of the handlers responds to the <code>GET</code> method, but differs in the path they<br>
are listening for. <code>routr</code> uses <code>reqres</code> under the hood so each handler method<br>
is passed a <code>Request</code> and <code>Response</code> pair (we’ll get back to the <code>keys</code><br>
argument). Lastly, each handler must return either <code>TRUE</code> indicating that the<br>
next route should be called, or <code>FALSE</code> indicating no further routes should be<br>
called. As the request and response objects are <code>R6</code> objects any changes to them<br>
will be kept outside of the handler and there is thus no need to return them.</p>
<p>Now, consider the situation where I have build my super fancy web service into a<br>
thriving business with millions of users – would I need to add a handler for<br>
every user? No. This would be a case for a parameterized path.</p>
<figure>
<pre><code data-lang="r"><span>route</span><span>$</span><span>add_handler</span><span>(</span><span>'get'</span><span>,</span><span> </span><span>'/users/:user_id'</span><span>,</span><span> </span><span>function</span><span>(</span><span>request</span><span>,</span><span> </span><span>response</span><span>,</span><span> </span><span>keys</span><span>,</span><span> </span><span>...</span><span>)</span><span> </span><span>{</span><span>
</span><span>response</span><span>$</span><span>status</span><span> </span><span>&lt;-</span><span> </span><span>200L</span><span>
</span><span>response</span><span>$</span><span>body</span><span> </span><span>&lt;-</span><span> </span><span>list</span><span>(</span><span>h</span><span>1</span><span> </span><span>=</span><span> </span><span>paste0</span><span>(</span><span>'This is the user information for '</span><span>,</span><span> </span><span>keys</span><span>$</span><span>user_id</span><span>))</span><span>
</span><span>TRUE</span><span>
</span><span>})</span><span>
</span><span>route</span></code></pre>
</figure>
<figure>
<pre><code data-lang="text">## A route with 3 handlers
## get: /users/thomasp85
##    : /users/:user_id
##    : /info</code></pre>
</figure>
<p>As can be seen, prefixing a path element with <code>:</code> will make it into a variable,<br>
matching anything that is put in there and adds it as an element to the <code>keys</code><br>
argument. Paths can contain as many variable elements as wanted in order to<br>
reuse handlers as efficiently as possible.</p>
<p>There’s a last piece of path functionality left to discuss: The wildcard. While<br>
parameterized path elements only matches as single element (e.g.<br>
<code>/users/:user_id</code> will match <code>/users/johndoe</code> but not <code>/users/johndoe/settings</code>)<br>
the wildcard matches anything. Let’s try one of these:</p>
<figure>
<pre><code data-lang="r"><span>route</span><span>$</span><span>add_handler</span><span>(</span><span>'get'</span><span>,</span><span> </span><span>'/setting/*'</span><span>,</span><span> </span><span>function</span><span>(</span><span>request</span><span>,</span><span> </span><span>response</span><span>,</span><span> </span><span>keys</span><span>,</span><span> </span><span>...</span><span>)</span><span> </span><span>{</span><span>
</span><span>response</span><span>$</span><span>status_with_text</span><span>(</span><span>403L</span><span>)</span><span> </span><span># Forbidden
</span><span> </span><span>FALSE</span><span>
</span><span>})</span><span>
</span><span>route</span><span>$</span><span>add_handler</span><span>(</span><span>'get'</span><span>,</span><span> </span><span>'/*'</span><span>,</span><span> </span><span>function</span><span>(</span><span>request</span><span>,</span><span> </span><span>response</span><span>,</span><span> </span><span>keys</span><span>,</span><span> </span><span>...</span><span>)</span><span> </span><span>{</span><span>
</span><span>response</span><span>$</span><span>status</span><span> </span><span>&lt;-</span><span> </span><span>404L</span><span>
</span><span>response</span><span>$</span><span>body</span><span> </span><span>&lt;-</span><span> </span><span>list</span><span>(</span><span>h</span><span>1</span><span> </span><span>=</span><span> </span><span>'We really couldn\'t find your page'</span><span>)</span><span>
</span><span>FALSE</span><span>
</span><span>})</span><span>
</span><span>route</span></code></pre>
</figure>
<figure>
<pre><code data-lang="text">## A route with 5 handlers
## get: /users/thomasp85
##    : /users/:user_id
##    : /setting/*
##    : /info
##    : /*</code></pre>
</figure>
<p>Here we add two new handlers, one preventing access to anything under the<br>
<code>/settings</code> location, and one implementing a custom <code>404 - Not found</code> page. Both<br>
returns <code>FALSE</code> as they are meant to prevent any further processing.</p>
<p>Now there’s a slight pickle with the current situation. If I ask for<br>
<code>/users/thomasp85</code> it can match three different handlers: <code>/users/thomasp85</code>,<br>
<code>/users/:user_id</code>, and <code>/*</code>. Which to chose? <code>routr</code> decides on the handler<br>
based on path specificity, where handlers are prioritized based on number of<br>
elements in the path (the more the better), number of parameterized elements<br>
(the less the better), and existence of wildcards (better with none). In the<br>
above case it means that the <code>/users/thomasp85</code> will be chosen. The handler<br>
priority can always be seen when printing the <code>Route</code> object.</p>
<p>The request method is less complicated than the path. It simply matches the<br>
method used in the request, ignoring the case. There’s one special method:<br>
<code>all</code>. This one will match any method, but only if a handler does not exist for<br>
that specific method.</p>
<h3>Route Stacks</h3>
<p>Conceptually, route stacks are much simpler than routes, in that they are just<br>
a sequential collection of routes, with the means to pass requests through them.<br>
Let’s create some additional routes and collect them in a <code>RouteStack</code>:</p>
<figure>
<pre><code data-lang="r"><span>parser</span><span> </span><span>&lt;-</span><span> </span><span>Route</span><span>$</span><span>new</span><span>()</span><span>
</span><span>parser</span><span>$</span><span>add_handler</span><span>(</span><span>'all'</span><span>,</span><span> </span><span>'/*'</span><span>,</span><span> </span><span>function</span><span>(</span><span>request</span><span>,</span><span> </span><span>response</span><span>,</span><span> </span><span>keys</span><span>,</span><span> </span><span>...</span><span>)</span><span> </span><span>{</span><span>
</span><span>request</span><span>$</span><span>parse</span><span>(</span><span>reqres</span><span>::</span><span>default_parsers</span><span>)</span><span>
</span><span>})</span><span>
</span><span>formatter</span><span> </span><span>&lt;-</span><span> </span><span>Route</span><span>$</span><span>new</span><span>()</span><span>
</span><span>formatter</span><span>$</span><span>add_handler</span><span>(</span><span>'all'</span><span>,</span><span> </span><span>'/*'</span><span>,</span><span> </span><span>function</span><span>(</span><span>request</span><span>,</span><span> </span><span>response</span><span>,</span><span> </span><span>keys</span><span>,</span><span> </span><span>...</span><span>)</span><span> </span><span>{</span><span>
</span><span>response</span><span>$</span><span>format</span><span>(</span><span>reqres</span><span>::</span><span>default_formatters</span><span>)</span><span>
</span><span>})</span><span>
</span><span>router</span><span> </span><span>&lt;-</span><span> </span><span>RouteStack</span><span>$</span><span>new</span><span>()</span><span>
</span><span>router</span><span>$</span><span>add_route</span><span>(</span><span>parser</span><span>,</span><span> </span><span>'request_prep'</span><span>)</span><span>
</span><span>router</span><span>$</span><span>add_route</span><span>(</span><span>route</span><span>,</span><span> </span><span>'app_logic'</span><span>)</span><span>
</span><span>router</span><span>$</span><span>add_route</span><span>(</span><span>formatter</span><span>,</span><span> </span><span>'response_finish'</span><span>)</span><span>
</span><span>router</span></code></pre>
</figure>
<figure>
<pre><code data-lang="text">## A RouteStack containing 3 routes
## 1: request_prep
## 2: app_logic
## 3: response_finish</code></pre>
</figure>
<p>Now, when our router receives a request it will first pass it to the parser<br>
route and attempt to parse the body. If it is unsuccessful it will abort (the<br>
<code>parse()</code> method returns <code>FALSE</code> if it fails), if not it will pass the request<br>
on to the route we build up in the prior section. If the chosen handler returns<br>
<code>TRUE</code> the request will then end up in the formatter route and the response body<br>
will be formatted based on content negotiation with the request. As can be seen<br>
route stacks are an effective way to extract common functionality into well<br>
defined handlers.</p>
<p>If you’re using <code>fiery</code>. <code>RouteStack</code> objects are also what will be used as<br>
plugins. Whether to use the router for <code>request</code>, <code>header</code>, or <code>message</code><br>
(WebSocket) events is decided by the <code>attach_to</code> field.</p>
<figure>
<pre><code data-lang="r"><span>app</span><span> </span><span>&lt;-</span><span> </span><span>fiery</span><span>::</span><span>Fire</span><span>$</span><span>new</span><span>()</span><span>
</span><span>app</span><span>$</span><span>attach</span><span>(</span><span>router</span><span>)</span><span>
</span><span>app</span></code></pre>
</figure>
<figure>
<pre><code data-lang="text">## 🔥 A fiery webserver
## 🔥  💥   💥   💥
## 🔥           Running on: 127.0.0.1:8080
## 🔥     Plugins attached: request_routr
## 🔥 Event handlers added
## 🔥              request: 1</code></pre>
</figure>
<h2>Predefined routes</h2>
<p>Lastly, <code>routr</code> comes with a few predefined routes, which I will briefly<br>
mention: The <code>ressource_route</code> maps files on the server to handlers. If you wish<br>
to serve static content in some way, this facilitates it, and takes care of a<br>
lot of HTTP header logic such as caching. It will also automatically serve<br>
compressed files if they exist and the client accepts them:</p>
<figure>
<pre><code data-lang="r"><span>static_route</span><span> </span><span>&lt;-</span><span> </span><span>ressource_route</span><span>(</span><span>'/'</span><span> </span><span>=</span><span> </span><span>system.file</span><span>(</span><span>package</span><span> </span><span>=</span><span> </span><span>'routr'</span><span>))</span><span>
</span><span>router</span><span>$</span><span>add_route</span><span>(</span><span>static_route</span><span>,</span><span> </span><span>'static'</span><span>,</span><span> </span><span>after</span><span> </span><span>=</span><span> </span><span>1</span><span>)</span><span>
</span><span>router</span></code></pre>
</figure>
<figure>
<pre><code data-lang="text">## A RouteStack containing 4 routes
## 1: request_prep
## 2: static
## 3: app_logic
## 4: response_finish</code></pre>
</figure>
<p>Now, you can get the package description file by visiting <code>/DESCRIPTION</code>. If a<br>
file is found it will return <code>FALSE</code> in order to simply return the file. If<br>
nothing is found it will return <code>TRUE</code> so that other routes can decide what to<br>
do.</p>
<p>If you wish to limit the size of requests, you can use the <code>sizelimit_route</code> and<br>
e.g. attach it to the <code>header</code> event in a <code>fiery</code> app, so that requests that are<br>
too big will get rejected before the body is fetched.</p>
<figure>
<pre><code data-lang="r"><span>sizelimit</span><span> </span><span>&lt;-</span><span> </span><span>sizelimit_route</span><span>(</span><span>10</span><span> </span><span>*</span><span> </span><span>1024</span><span>^</span><span>2</span><span>)</span><span> </span><span># 10 mb
</span><span>reject_router</span><span> </span><span>&lt;-</span><span> </span><span>RouteStack</span><span>$</span><span>new</span><span>(</span><span>size</span><span> </span><span>=</span><span> </span><span>sizelimit</span><span>)</span><span>
</span><span>reject_router</span><span>$</span><span>attach_to</span><span> </span><span>&lt;-</span><span> </span><span>'header'</span><span>
</span><span>app</span><span>$</span><span>attach</span><span>(</span><span>reject_router</span><span>)</span><span>
</span><span>app</span></code></pre>
</figure>
<figure>
<pre><code data-lang="text">## 🔥 A fiery webserver
## 🔥  💥   💥   💥
## 🔥           Running on: 127.0.0.1:8080
## 🔥     Plugins attached: request_routr
## 🔥                       header_routr
## 🔥 Event handlers added
## 🔥               header: 1
## 🔥              request: 1</code></pre>
</figure>
<h2>Wrapping up</h2>
<p>As I started by saying, the release of <code>routr</code> marks a point of maturity for my<br>
<code>fiery</code> ecosystem. I’m extremely happy with this, but it is in no way the end of<br>
development. I will pivot to working on more specialized plugins now concerned<br>
with areas such as security and scalability, but the main approach to building<br>
<code>fiery</code> server side logic is now up and running – I hope you’ll take it for a<br>
spin.</p>
	<p>
<div>
<div>To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="http://www.data-imaginist.com/2017/Introducing-routr/"> Data Imaginist - R posts</a></strong>.</div>
<hr>
<a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers.com</a> offers <strong><a rel="nofollow" href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers">daily e-mail updates</a></strong> about <a rel="nofollow" href="https://www.r-project.org/" title="The R Project for Statistical Computing">R</a> news and <a rel="nofollow" href="https://www.r-bloggers.com/search/tutorial" title="R tutorials">tutorials</a> on topics such as: <a rel="nofollow" href="https://www.r-bloggers.com/search/data%20science" title="Data science">Data science</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/Big%20Data" title="Big Data">Big Data, <a rel="nofollow" href="https://www.r-users.com/" title="R jobs">R jobs</a>, visualization (<a rel="nofollow" href="https://www.r-bloggers.com/search/ggplot2" title="ggplot and ggplot2 tutorials">ggplot2</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/boxplot" title="Boxplots using lattice and ggplot2 tutorials">Boxplots</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/map" title="Maps and gis">maps</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/animation" title="Animation in R">animation</a>), programming (<a rel="nofollow" href="https://www.r-bloggers.com/search/RStudio" title="RStudio IDE for R">RStudio</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/sweave" title="Sweave and literate programming">Sweave</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/LaTeX" title="LaTeX in R">LaTeX</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/SQL" title="SQL and databases">SQL</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/eclipse" title="Eclipse IDE for R">Eclipse</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/git" title="git and github, Version Control System">git</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/hadoop" title="Large data in R using Hadoop">hadoop</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/Web+Scraping" title="Web Scraping of google, facebook, yahoo, twitter and more using R">Web Scraping</a>) statistics (<a rel="nofollow" href="https://www.r-bloggers.com/search/regression" title="Regressions and ANOVA analysis tutorials">regression</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/PCA" title="principal component analysis tutorial">PCA</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/time+series" title="Time series">time series</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/trading" title="finance trading">trading</a>) and more...
<img width="1" alt="" src="http://feeds.feedburner.com/~r/RBloggers/~4/BNhNJSltwUw" height="1"></a></div>