ace v0.13.1 Ace.HTTP2.Service View Source

Run a supervised tree of HTTP/2.0 servers, all available on a single port.

The task associated with each client request is processed by an application level worker. To start workers a module and start arguments are required.

For example, given {MyProject.WWW, ["foo"]}, Ace will start workers by executing the following. MyProject.WWW.start_link("foo")

Example application:

defmodule MyProject.WWW do
  use GenServer
  alias Ace.HTTP2.Server

  def start_link(greeting) do
    GenServer.start_link(__MODULE__, greeting)
  end

  def handle_info({stream, %Ace.Request{method: :GET, path: "/"}}, greeting) do
    response = Ace.Response.new(200, [], greeting)
    Server.send_response(stream, response)
    {:stop, greeting, :normal}
  end
  def handle_info({stream, _request}, greeting) do
    response = Ace.Response.new(404, [], false)
    Server.send_response(stream, response)
    {:stop, greeting, :normal}
  end
end

This module has a start_link/1 function and so can be started by as a service as follows.

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)
  • See start_link/2 for the full list of options available when starting a service

Each client request defines an independant stream. Each stream is handled by an isolated worker process running the application.

Supervising services

Ace makes it easy to start multiple services in a single Mix project. Starting a service returns the service supervisor.

This supervisor may act as the application supervisor if it is the only one started.

defmodule MyProject.Application do
  @moduledoc false

  use Application

  def start(_type, _args) do

    certfile = Application.app_dir(:my_project, "/priv/cert.pem")
    keyfile = Application.app_dir(:my_project, "/priv/key.pem")

    Ace.HTTP2.Service.start_link(
      {MyProject.WWW, ["Hello, World!"]},
      port: 8443,
      certfile: certfile,
      keyfile: keyfile
    )
  end
end

An Ace.HTTP2.Service can also exist as one of a group of supervisors.

defmodule MyProject.Application do
  @moduledoc false

  use Application

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

    www_certfile = Application.app_dir(:my_project, "/priv/www/cert.pem")
    www_keyfile = Application.app_dir(:my_project, "/priv/www/key.pem")

    www_app = {MyProject.WWW, ["Hello, World!"]}
    www_opts = [port: 8443, certfile: www_certfile, keyfile: www_keyfile]

    api_certfile = Application.app_dir(:my_project, "/priv/api/cert.pem")
    api_keyfile = Application.app_dir(:my_project, "/priv/api/key.pem")

    api_app = {MyProject.WWW, ["Hello, World!"]}
    api_opts = [port: 8443, certfile: api_certfile, keyfile: api_keyfile]

    children = [
      supervisor(Ace.HTTP2.Service, [www_app, www_opts]),
      supervisor(Ace.HTTP2.Service, [api_app, api_opts]),
      worker(MyProject.worker, [arg1, arg2, arg3]),
    ]
  end
end

Testing endpoints

Starting a service on port 0 will rely on the operating system to allocate an available port. This allows services to be stood up for individual tests, perhaps all with different configuration.

To find the port that a service has started using a process may be given as the optional owner. Once the service has started the owner receives a message with format.

{:listening, service_pid, port}

This can be used to setup test services.

opts = [port: 0, owner: self(), certfile: certfile, keyfile: keyfile]
assert {:ok, service} = Service.start_link({TestApp, [:test_config]}, opts)
assert_receive {:listening, ^service, port}

# Use general purpose client libraries to test service available at `port`

This can be seen in action in the test directories of this project.

Link to this section Summary

Functions

Start an endpoint to accept HTTP/2.0 connections

Link to this section Functions

Link to this function start_link(application, options) View Source

Start an endpoint to accept HTTP/2.0 connections