README.md

# Raxx: an Elixir webserver interface

## What is Raxx?

1. An interface specification for Elixir webservers and Elixir application.
2. A set of tools to help develop Raxx-compliant web applications

[Documentation for Raxx is available online](https://hexdocs.pm/raxx)

[Introductory talk I gave at Elixir.LDN](https://www.youtube.com/watch?v=80AXtvXFIA4&index=2&list=PLWbHc_FXPo2ivlIjzcaHS9N_Swe_0hWj0)

## Hello, World!

Add raxx to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [{:raxx, "~> 0.4.1"}]
end
```

Define the behaviour of your servers.

```elixir
defmodule HelloWeb.Server do
  import Raxx.Response

  def handle_request(%{path: []}, _env) do
    ok("Hello, World!")
  end

  def handle_request(%{path: [name]}, _env) do
    ok("Hello, #{name}!")
  end

  def handle_request(%{path: _unknown}, _env) do
    not_found()
  end
end
```

Mount your server in you application. *Example using [Ace](https://github.com/CrowdHailer/Ace)*

```elixir
defmodule HelloWeb do
  use Application

  @raxx_app {HelloWeb.Server, []}

  def start(_type, _args) do
    import Supervisor.Spec, warn: false

    children = [
      worker(Ace.TCP, [{Raxx.Adapters.Ace.Handler, @raxx_app}, [port: 8080]])
    ]

    opts = [strategy: :one_for_one]
    Supervisor.start_link(children, opts)
  end
end
```

Raxx currently has adapters for three erlang servers.
- [HelloElli example](https://github.com/CrowdHailer/raxx/tree/master/example/hello_elli).
- [cowboy example](https://github.com/CrowdHailer/raxx/tree/master/example/cowboy_example).

### Principles

- Stateless HTTP request fulfill a valuable role in modern applications and will continue to do so, this simple usecase must not be compicated by catering to more advanced communication patterns.
- Use Ruby rack and Clojure ring as inspiration, but be happy to break away from historic CGI-style header names.
- Surface utilities so that it can be used in general HTTP based applications, a RFC6265 module could be used by plug and rack
- Be a good otp citizen, work well in an umbrella app,
- Raxx is designed to be the foundation of a VC (view controller) framework. Other applications in the umbrella should act as the model.
- [Your server as a function](https://monkey.org/~marius/funsrv.pdf)
- No support for working with errors, throws, exits. We handle them in debugging because elixir is a dynamic language but they should not be used for routing or responses other than 500.

*Raxx is inspired by the [Ruby's Rack interface](http://rack.github.io/) and [Clojure's Ring interface](https://github.com/ring-clojure).*

## Raxx applications

A Raxx application module has a `handle_request/2` function that takes a `Raxx.Request` and an application environment, as arguments.
For every incomming HTTP connnection `handle_request/2` is called.

The application may indicate to the server that it should respond with a simple HTTP response buy returning a `Raxx.Response` struct.

```elixir
defmodule MySimpleApp do
  def handle_request(_r, _env), do: Raxx.Response.ok()
end
```

Alternativly the the application may indicate that the connection should be upgraded.
In the case of an upgrade the returned upgrade object specifies the communication protocol required.

```elixir
defmodule MyChunkingApp do
  def handle_request(_r, env), do: Raxx.Chunked.upgrade({__MODULE__, env})
  def handle_info(message, _env), do: {:send, "ping"}
end
```

Currently the following upgraded protocols are supported, with others (such as websockets), in development.

- HTTP Chunked
- Server Sent Events

### Raxx.Request

`Raxx.Request`

HTTP requests to a Raxx application are encapsulated in a `Raxx.Request` struct.

```elixir
%Raxx.Request{
  host: "www.example.com",
  path: ["some", "path"],
  ...
}
```

Data can easily be read from the request directly and through pattern matching.
This allows for expressive routing in raxx apps without a routing DSL.
The hello world example is a great example of this.

The `Raxx.Request` module provides additional functionality for inspect the request.
For example inspecting cookies.

```elixir
defmodule Router do
  import Raxx.Request

  def handle_request(request = %{path: ["api" | rest]}, env) do
    ApiRouter.handle_request(%{request | path: rest}, env)
  end

  def handle_request(request = %{path: ["users"], method: method}, _env) do
    case method do
      :GET ->
        query = request.query
        # Get all the users that match a query
      "POST" ->
        data = request.body
        # Create a user with the following data
      "PATCH" ->
        user_id = parse_cookies(request)["user-id"]
        # Update a the details of the user from a cookie session
    end
  end
end
```

To see the details of each request object checkout the [cowboy example](https://github.com/CrowdHailer/raxx/tree/master/example/cowboy_example).

### Raxx.Response

`Raxx.Response`

Any map with the required keys (`:status`, `:headers`, `:body`) can be interpreted by the server as a simple HTTP response.
However it is more usual to return a `Raxx.Response` struct which has sensible defaults for all fields.

Manually creating response structs can be tedious.
The `Raxx.Response` module has several helpers for creating response maps.
This include setting status codes, manipulating cookies

```elixir
defmodule FooRouter do
  alias Raxx.Response
  def handle_request(%{path: ["users"], method: :GET}, _env) do
    Response.ok("All users: Andy, Bethany, Clive")
  end

  def handle_request(%{path: ["users"], method: "POST", body: data}, _env) do
    case MyApp.create_user(data) do
      {:ok, user} -> Response.created("New user #{user}")
      {:error, :already_exists} -> Response.conflict("sorry")
      {:error, :bad_params} -> Response.bad_request("sorry")
      {:error, :database_fail} -> Response.bad_gateway("sorry")
      {:error, _unknown} -> Response.internal_server_error("Well that's weird")
    end
  end

  def handle_request(%{path: ["users"], method: _}, _env) do
    Response.method_not_allowed("Don't do that")
  end

  def handle_request(%{path: ["users", id], method: :GET}, _env) do
    case MyApp.get_user(id) do
      {:ok, user} -> Response.ok("New user #{user}")
      {:error, nil} -> Response.not_found("User unknown")
      {:error, :deleted} -> Response.gone("User deleted")
    end
  end

  def handle_request(_request, _env) do
    Response.not_found("Sorry didn't get that")
  end
end
```

### Raxx.Chunked

`Raxx.Chunked` allows data to be streamed to the client.
An unbounded amount of response data may be sent this way.

A Raxx application that returns a `Raxx.Chunked` struct from a call to `handle_request/2`, is indicating that it wishes to send the response in chunks.

```elixir
%Raxx.Chunked{
  app: {MyHandler, :none},
  ...
}
```

A chunked handler must implement a `handle_info/2` callback.
This callback is called everytime the request process recieves a message, taking the message and environment as arguments.

```elixir
defmodule Ping do
  def handler_request(_, _), do: Raxx.Chunked.upgrade({__MODULE__, nil})

  def handle_info({:data, chunk}), do: {:send, chunk}
  def handle_info({_), do: :nosend
end
```

### Raxx.ServerSentEvents

See sever sent events in examples directory.

```elixir
defmodule ServerSentEvents.Router do
  alias Raxx.Response
  alias Raxx.ServerSentEvents, as: SSE

  def handle_request(%{path: [], method: :GET}, _opts) do
    Response.ok(home_page)
  end

  def handle_request(%{path: ["events"], method: :GET}, env) do
    Process.send_after(self, 0, 1000)
    SSE.upgrade(__MODULE__, env, %{initial: "hello"})
  end

  def handle_request(_request, _opts) do
    Response.not_found("Page not found")
  end

  # handle_info
  def handle_info(10, _opts) do
    {:send, ""}
  end
  def handle_info(i, _opts) when rem(i, 2) == 0 do
    Process.send_after(self, i + 1, 1000)
    chunk = SSE.Event.new("#{i}", event: "count") |> SSE.Event.to_chunk
    {:send, chunk}
  end
  def handle_info(i, _opts) do
    Process.send_after(self, i + 1, 1000)
    :nosend
  end

  defp home_page do
    """
    The page. see example.
    """
  end
end
```

[Link to implementing server in node.js](http://www.html5rocks.com/en/tutorials/eventsource/basics/)

[HTML living standard](https://html.spec.whatwg.org/multipage/comms.html#server-sent-events)

<!-- ### Raxx.Test

`Raxx.Test`

This module provides helpers for testing Raxx applications.

```elixir
test "hello app says hi" do
  request = Raxx.Test.get("")
  response = MyApp.handle_request(request, :no_env)
  assert 200 = response.status
  assert "Hello, World!" = response.body
end
``` -->

## Contributing

If you have Elixir installed on your machine then you can treat this project as a normal mix project and run tests via `mix test`.

If required a development environment can be created using [Vagrant](www.vagrantup.com).