Raxx is an alternative to Plug and is inspired by projects such as Rack(Ruby) and Ring(Clojure).
1.0-rc.1
is now available. To use it requires a server, the examples here can be experimented with by adding{:ace, "~> 0.15.2"}
to a projects dependencies, NOTE1.0-rc.0
is equivalent to0.14.0
Raxx models HTTP as message passing between client and server. There are two flavours to this model.
First is the simple case where a complete request is a single message from client to server and a complete response as a single message from server to client. This is the model successfully exploited by Rack.
The second cases is for streaming where a series of messages are sent in either/both directions. For HTTP this series of messages begins with a head, consisting of all header information plus a few extra details (path for request, status for response). There is then the body broken into 1 or more parts and finally a tail which may contain no further information or may include some trailers.
Simple
request -->
Client ============================================ Server
<-- response
A server can implement the handle_request/2
callback to use this model in the simplest cases.
For example:
defmodule HomePage do
use Raxx.Server
@impl Raxx.Server
def handle_request(%{method: :GET, path: []}, _state) do
response(:ok)
|> set_header("content-type", "text/plain")
|> set_body("Hello, World!")
end
end
Streaming
tail | data(1+) | head(request) -->
Client ============================================ Server
<-- head(response) | data(1+) | tail
A server can implement the handle_head/2
, handle_data/2
& handle_tail/2
callbacks to react to parts of the request as they become available
defmodule Upload do
use Raxx.Server
@impl Raxx.Server
def handle_head(%{method: :PUT, path: ["upload"] body: true}, _state) do
{:ok, io_device} = File.open("my/path")
{[], {:file, device}}
end
@impl Raxx.Server
def handle_data(data, state = {:file, device}) do
IO.write(device, data)
{[], state}
end
@impl Raxx.Server
def handle_tail(_trailers, state) do
response(:see_other)
|> set_header("location", "/")
end
end
Raxx.Server
has one final callback handle_info/2
for dealing with updates from other processes within the application. Several more examples are available in the README.
Goals
Raxx is designed as an alternative to Plug but not a replacement. This thread is meant to be just about Raxx developments, see this previous topic for why I started developing an alternative to plug.
- model the communication between Client and Server as messaging to be consistent with the erlang/Elixir world view.
This is the main difference to plug which uses a connection model. - support all HTTP patterns including streaming of requests and/or responses.
In this way it is an evolution of Rack. - Require only pure functions, even when implementing a stateful server.
Familiar anyone who has implemented aGenServer
.