Ace: HTTP/2 webserver

Ace 0.11.1 includes a HTTP/2.0 client with streaming or synchronous api.

Here is a quick example. For full story see the docs

alias Ace.HTTP2.Client
alias Ace.Request
alias Ace.Response

{:ok, client} = Client.start_link("http2.golang.org")

# simple
request = Request.get("/", [{"accept", "application/json"}])
{:ok, response} = Client.send_sync(client, request)

# streaming
request = Request.get("/", [{"accept", "application/json"}])
{:ok, stream} = Client.stream(client, request)

receive do
  {^stream, response = %Response{body: true}} ->
    IO.inspect(response.status)
end
receive do
  {^stream, %{data: data, end_stream: end_stream}} ->
    IO.inspect(data)
end
2 Likes

I talked about Ace at Elixir.LDN, the videos is available now.

The talk covers

  • The basics of HTTP/2
  • Design decisions made by Ace, in particular how to make streaming easy
  • Some chosen implementation details
  • Next steps, including a plug adapter, GRPC implementation and GenStage integration
5 Likes

Ace 0.14.0 released

Switches over to using the Latest version of the Raxx.Server interface, which supports streaming.

Raxx docs.

Example

Server specification

defmodule MyProject.WWW do
  use Raxx.Server

  def handle_headers(%Raxx.Request{method: :GET, path: []}, greeting) do
    Raxx.response(:ok)
    |> Raxx.set_header("content-type", "text/plain")
    |> Raxx.set_body(greeting)
  end
end

Server startup

application = {MyProject.WWW, "Hello, World!"}

options = [
  port: 8443,
  certfile: "path/to/certfile"
  keyfile: "path/to/keyfile"
]
{:ok, pid} = Ace.HTTP2.Service.start_link(application, options)
1 Like

Ace 0.14.6 released.

Added is the Ace.HTTP.Service. This is the general purpose way start a server, currently supports HTTP/1 but will serve the same content over HTTP/1 and HTTP/2 in next releases

This functionality obsoletes the ace_http previously for serving content over HTTP/1

1 Like

Ace 0.14.8 Supports HTTP/2 upgrade.

Application layer protocol notification(ALPN) support now means that a single endpoint will serve content over HTTP/1 or HTTP/2 dependant on the client. This was the final step to make Ace useful to most web applications. Test it out by using Ace.HTTP.Service.

3 Likes

Ace 0.15.4 Uses latest version of server interface with better support for streaming.

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
2 Likes

Ace 0.15.8 Released

  • Fixes all known issues with connection handling processes not terminating.
  • Add’s typespecs to key public functions, uses typespecs added to latest version of Raxx
  • Adds support for OPTIONS TRACE and CONNECT methods.

Also Ace.HTTP.Service now has a child_spec so it can be added to supervision trees in the up to date manner

1 Like

Ace 0.15.9 Released

Default start_link and child_spec functionality can be set up by using Ace.HTTP.Service

New

defmodule MyApp do
  use Ace.HTTP.Service, [port: 8080, cleartext: true]

  def handle_request(%{method: :GET, path: []}, %{greeting: greeting}) do
    response(:ok)
    |> set_header("content-type", "text/plain")
    |> set_body("#{greeting}, World!")
  end
end

config = %{greeting: "Hello"}

MyApp.start_link(config, [port: 1234])
# [info] Serving cleartext using HTTP/1 and HTTP/2 on port 1234
# => {:ok, service}

Add to supervision tree

children = [
  {MyApp, [%{greeting: "Hello"}, [port: 1234]]},
]

Previously

defmodule MyApp do
  use Raxx.Server

  @impl Raxx.Server
  def handle_request(%{method: :GET, path: []}, %{greeting: greeting}) do
    response(:ok)
    |> set_header("content-type", "text/plain")
    |> set_body("#{greeting}, World!")
  end
end

application = {MyApp, %{greeting: "Hello"}}
options = [port: 1234, cleartext: true]

Ace.HTTP.Service.start_link(application, options)
# [info] Serving cleartext using HTTP/1 and HTTP/2 on port 1234
# => {:ok, service}
2 Likes

Major release 0.16.0

Needed to use the latest version of Raxx Raxx - Interface for HTTP webservers, frameworks and clients (1.0 now released!)

1 Like

Access to underlying connection information added 0.16.1

For both HTTP/1 and HTTP/2 it is now possible to access connection information from a worker
Process.get(Ace.HTTP.Connection). This allows inspection and verification of peer certificate. For example allowing a pure Elixir implementation of GRPC.

Access to raw path added in 0.16.3

Gives full control to the user over their applications routing. as well as making it easy to verify the signature of requests.

1 Like

Better errors and child_specs added in 0.16.4 to help get started.

1. Callback errors

Raxx.ReturnError added to help debug callbacks. Now if a callback returns an invalid value the following error will be raise.

** (ReturnError) Invalid reaction from server module. Response must be complete or include update server state

    e.g.
      # Complete
      Raxx.response(:ok)
      |> Raxx.set_body("Hello, World!")

      # New server state
      response = Raxx.response(:ok)
      |> Raxx.set_body(true)
      {[response], new_state}

    Actual value returned was
      :ok

2. Server child_specs

If a module using Ace.HTTP.Service defines all the required server options it can be added to a supervision tree using a simpler child spec.

# lib/my_app/www.ex
defmodule MyApp.WWW do
  use Ace.HTTP.Service, [port: 8080, cleartext: true]
end

# lib/my_app/application.ex
children = [
  {MyApp.WWW, [%{config: foo}]}
  # or
  MyApp.WWW
]
3 Likes

Count active connections to a service added in 0.16.6

Thanks to @arturcygan for this contribution.

2 Likes

Bug when receiving multiple headers fixed in 0.16.7

0.16.8 released

Added

  • Formatter check to travis.
  • Dialyzer check to travis.
  • HTTP/1 request backpressure.

Changed

  • Internal updates to use serialization and parsing from Raxx project

Fixed

  • Exit with status :normal in case where set_active call is made on closed socket.
2 Likes

0.17.0 released

Updated to include the changes to the Raxx interface released in Raxx 0.16.0.
See this thread for release notes.

1 Like

0.18.0 release to separate streaming from simple interface.

Ace HTTP service must now explicitly implement Raxx.SimpleServer or Raxx.Server.

e.g.

defmodule MyApp.WWW do
  use Ace.HTTP.Service, port: 8080, cleartext: true
  use Raxx.SimpleServer

  @impl Raxx.SimpleServer
  def handle_request(_,_) do
    response(:ok)
  end
end

This changes are to facilitate adding middleware to Raxx.

1 Like

Erroneous warnings from use Raxx.SimpleServer fixed in 0.18.2

Small but helpful fix this one. Thanks ishikawa for providing the solution.

1 Like

0.18.3 Fix HTTP/2 handling for application data sent as iolist

See CHANGELOG

1 Like

0.18.5 released

This fixes parsing of te headers in HTTP/2 requests

1 Like