Skip to content

Commit

Permalink
Move overview to its own .md
Browse files Browse the repository at this point in the history
(and reference it from README.md)
  • Loading branch information
paulo-ferraz-oliveira committed Aug 2, 2023
1 parent 37df5e9 commit 6b0a4d1
Show file tree
Hide file tree
Showing 3 changed files with 222 additions and 42 deletions.
167 changes: 167 additions & 0 deletions OVERVIEW.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# Elli - Overview

Copyright (c) 2012-2016 Knut Nesheim, 2016-2018 elli-lib team

__Version:__ 3.3.0

__Authors:__ Knut Nesheim, elli-lib team.

Erlang web server for HTTP APIs

## Features

Here's the features Elli _does_ have:

* [Rack][]-style request-response. Your handler function gets a
complete request and returns a complete response. There's no
messaging, no receiving data directly from the socket, no writing
responses directly to the socket. It's a very simple and
straightforward API. Have a look at [`elli_example_callback`](elli_example_callback.html)
for examples.

* Middlewares allow you to add useful features like compression,
encoding, stats, but only have it used when needed. No features you
don't use on the critical path.

* Short-circuiting of responses using exceptions, allows you to use
"assertions" that return for example 403 permission
denied. `is_allowed(Req) orelse throw({403, [], <<"Permission
denied">>})`.

* Every client connection gets its own process, isolating the failure
of a request from another. For the duration of the connection, only
one process is involved, resulting in very robust and efficient
code.

* Binaries everywhere for strings.

* Instrumentation inside the core of the webserver, triggering user
callbacks. For example when a request completes, the user callback
gets the `request_complete` event which contains timings of all the
different parts of handling a request. There's also events for
clients unexpectedly closing a connection, crashes in the user
callback, etc.

* Keep alive, using one Erlang process per connection only active
when there is a request from the client. Number of connections is
only limited by RAM and CPU.

* Chunked transfer in responses for real-time push to clients

* Basic pipelining. HTTP verbs that does not have side-effects(`GET`
and `HEAD`) can be pipelined, ie. a client supporting pipelining
can send multiple requests down the line and expect the responses
to appear in the same order as requests. Elli processes the
requests one at a time in order, future work could make it possible
to process them in parallel.

* SSL using built-in Erlang/OTP ssl, nice for low volume admin
interfaces, etc. For high volume, you should probably go with
nginx, stunnel or ELB if you're on AWS.

* Implement your own connection handling, for WebSockets, streaming
uploads, etc. See [`elli_example_callback_handover`](elli_example_callback_handover.html).

## Extensions

Here's some ready-to-use extensions for Elli.

* [elli_access_log](https://github.com/elli-lib/elli_access_log):
Access log
* [elli_basicauth](https://github.com/elli-lib/elli_basicauth):
Basic auth
* [elli_chatterbox](https://github.com/elli-lib/elli_chatterbox):
HTTP/2 support
* [elli_cloudfront](https://github.com/elli-lib/elli_cloudfront):
CloudFront signed URLs
* [elli_cookie](https://github.com/elli-lib/elli_cookie):
Cookies
* [elli_date](https://github.com/elli-lib/elli_date):
"Date" header
* [elli_fileserve](https://github.com/elli-lib/elli_fileserve):
Static content
* [elli_prometheus](https://github.com/elli-lib/elli_prometheus):
Prometheus
* [elli_stats](https://github.com/elli-lib/elli_stats):
Real-time statistics dashboard
* [elli_websockets](https://github.com/elli-lib/elli_websocket):
WebSockets
* [elli_xpblfe](https://github.com/elli-lib/elli_xpblfe):
X-Powered-By LFE

You can also find a more complete list at <https://github.com/elli-lib>.

## About

From operating and debugging high-volume, low-latency apps we have
gained some valuable insight into what we want from a webserver. We
want simplicity, robustness, performance, ease of debugging,
visibility into strange client behaviour, really good instrumentation
and good tests. We are willing to sacrifice almost everything, even
basic features to achieve this.

With this in mind we looked at the big names in the Erlang
community: [Yaws][], [Mochiweb][], [Misultin][] and [Cowboy][]. We
found [Mochiweb][] to be the best match. However, we also wanted to
see if we could take the architecture of [Mochiweb][] and improve on
it. Elli takes the acceptor-turns-into-request-handler idea found
in [Mochiweb][], the binaries-only idea from [Cowboy][] and the
request-response idea from [WSGI][]/[Rack][] (with chunked transfer
being an exception).

On top of this we built a handler that allows us to write HTTP
middleware modules to add practical features, like compression of
responses, HTTP access log with timings, a real-time statistics
dashboard and chaining multiple request handlers.

## Aren't there enough webservers in the Erlang community already?

There are a few very mature and robust projects with steady
development, one recently ceased development and one new kid on the
block with lots of interest. As Elli is not a general purpose
webserver, but more of a specialized tool, we believe it has a very
different target audience and would not attract effort or users away
from the big names.

## Why another webserver? Isn't this just the NIH syndrome?

[Yaws][], [Mochiweb][], [Misultin][], and [Cowboy][] are great
projects, hardened over time and full of very useful features for web
development. If you value developer productivity, [Yaws][] is an
excellent choice. If you want a fast and lightweight
server, [Mochiweb][] and [Cowboy][] are excellent choices.

Having used and studied all of these projects, we believed that if we
merged some of the existing ideas and added some ideas from other
communities, we could create a core that was better for our use cases.

It started out as an experiment to see if it is at all possible to
significantly improve and it turns out that for our particular use
cases, there is enough improvement to warrant a new project.

## What makes Elli different?

Elli has a very simple architecture. It avoids using more processes
and messages than absolutely necessary. It uses binaries for
strings. The request-response programming model allows middlewares to
do much heavy lifting, so the core can stay very simple. It has been
instrumented so as a user you can understand where time is spent. When
things go wrong, like the client closed the connection before you
could send a response, you are notified about these things so you can
better understand your client behaviour.

## Performance

"Hello World!" micro-benchmarks are really useful when measuring the
performance of the webserver itself, but the numbers usually do more
harm than good when released. I encourage you to run your own
benchmarks, on your own hardware. Mark Nottingham has some
[very good pointers](http://www.mnot.net/blog/2011/05/18/http_benchmark_rules)
about benchmarking HTTP servers.

[Yaws]: https://github.com/klacke/yaws
[Mochiweb]: https://github.com/mochi/mochiweb
[Misultin]: https://github.com/ostinelli/misultin
[Cowboy]: https://github.com/ninenines/cowboy
[WSGI]: https://www.python.org/dev/peps/pep-3333/
[Rack]: https://github.com/rack/rack
92 changes: 52 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# elli - Erlang web server for HTTP APIs
# Elli - Erlang web server for HTTP APIs

[![Hex.pm](https://img.shields.io/hexpm/v/elli.svg)](https://hex.pm/packages/elli)
[![Documentation](https://img.shields.io/badge/docs-edown-green.svg)](doc/README.md)
Expand All @@ -8,37 +8,36 @@
[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

Elli is a webserver you can run inside your Erlang application to
expose an HTTP API. Elli is aimed exclusively at building
expose an HTTP API. It is aimed exclusively at building
high-throughput, low-latency HTTP APIs. If robustness and performance
is more important than general purpose features, then `elli` might be
is more important to you than general purpose features, then Elli might be
for you. If you find yourself digging into the implementation of a
webserver, `elli` might be for you. If you're building web services,
not web sites, then `elli` might be for you.
webserver, then Elli might be for you. If you're building web services,
not web sites, then Elli might be for you.

Elli requires OTP 20.0 or newer.

## Installation

To use `elli` you will need a working installation of Erlang 18.0 (or later).

Add `elli` to your application by adding it as a dependency to your
[`rebar.config`](http://www.rebar3.org/docs/configuration):
Add `elli` to your application as a dependency to your
[`rebar.config`](https://www.rebar3.org/docs/configuration):

```erlang
{deps, [
%% ...
{elli, "3.0.0"}
{elli, "3.3.0"}
]}.
```

Afterwards you can run:
Afterwards, to compile it, you can run:

```console
rebar3 compile
```

## Usage

To boot Elli inside an Erlang shell, run:

```console
rebar3 shell
```
Expand All @@ -53,67 +52,80 @@ rebar3 shell
### Callback Module

The best source to learn how to write a callback module
is [`elli_example_callback.erl`](elli_example_callback.html). There are a bunch
is [`elli_example_callback`](elli_example_callback.html).
There are also a bunch
of examples used in the tests as well as descriptions of all the events.

A minimal callback module could look like this:
A minimal callback module looks something like this:

```erlang
-module(elli_minimal_callback).
-export([handle/2, handle_event/3]).
-behaviour(elli_handler).

-include_lib("elli/include/elli.hrl").
-behaviour(elli_handler).

-export([handle/2, handle_event/3]).

handle(Req, _Args) ->
%% Delegate to our handler function
handle(Req#req.method, elli_request:path(Req), Req).
Method = Req#req.method,
Path = elli_request:path(Req),
handle(Method, Path, Req).

handle('GET',[<<"hello">>, <<"world">>], _Req) ->
handle('GET' = _Method, [<<"hello">>, <<"world">>] = _Path, _Req) ->
%% Reply with a normal response. `ok' can be used instead of `200'
%% to signal success.
{ok, [], <<"Hello World!">>};
StatusCode = ok,
Headers = [],
Body = <<"Hello World!">>,
{StatusCode, Headers, Body};

handle(_, _, _Req) ->
handle(_Method, _Path, _Req) ->
{404, [], <<"Not Found">>}.

%% @doc Handle request events, like request completed, exception
%% @doc Handle request events: request completed, exception
%% thrown, client timeout, etc. Must return `ok'.
handle_event(_Event, _Data, _Args) ->
ok.
```

### Supervisor Childspec
### Supervisor ChildSpec

To add `elli` to a supervisor you can use the following example and adapt it to
your needs.

```erlang
-module(fancyapi_sup).
-module(elli_minimal_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1]).

start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
-export([start_link/0, init/1]).

init([]) ->
ElliOpts = [{callback, fancyapi_callback}, {port, 3000}],
start_link() ->
SupName = {local, ?MODULE},
Module = ?MODULE,
Args = [],
supervisor:start_link(SupName, Module, Args).

init([] = _Args) ->
ElliOpts = [
{callback, elli_minimal_callback},
{port, 3000}
],
ElliSpec = {
fancy_http,
{elli, start_link, [ElliOpts]},
permanent,
5000,
worker,
[elli]},

{ok, { {one_for_one, 5, 10}, [ElliSpec]} }.
_Id = elli_minimal_http,
_Start = {elli, start_link, [ElliOpts]},
_Restart = permanent,
_Shutdown = 5000,
_Worker = worker,
_Modules = [elli]},

{ok, {{_Strategy = one_for_one, _Intensity = 5, _Period = 10}, [ElliSpec]} }.
```

## Further Reading
## Further reading

For more information about the features and design philosophy of `elli` check
out the [overview](doc/README.md).
For more information about the features and design philosophy of Elli check
out the [`overview`](overview.html).

## License

Expand Down
5 changes: 3 additions & 2 deletions rebar.config
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@
]}.
{ex_doc, [
{extras, [
"README.md",
"OVERVIEW.md",
"CHANGELOG.md",
"LICENSE",
"README.md"
"LICENSE"
]},
{main, "README.md"},
{source_url, "https://github.com/elli-lib/elli"}
Expand Down

0 comments on commit 6b0a4d1

Please sign in to comment.