Skip to content

Server adapters

A server adapter bridges a concrete HTTP server to the framework. Its one job is the abstract contract: a request comes in, a status / headers / body go out. Because every adapter wraps the same endpoint app, swapping the underlying server (production Cro, or the in-memory test driver) changes only which adapter you construct.

The adapter contract

MVC::Keayl::Adapter is a role carrying the built app and turning a Request into the finalized response tuple:

1
my ($status, $headers, $body) = $adapter.handle($request);

handle calls the app and finalizes the Response, so a concrete adapter only has to translate its server's native request and response types to and from the framework's.

Test adapter

MVC::Keayl::Adapter::Test drives the whole stack in memory (no socket), which makes it the tool for testing controllers, middleware, and routes:

1
2
3
4
5
6
use MVC::Keayl::Adapter::Test;

my $adapter = MVC::Keayl::Adapter::Test.new(:app($built-app));

my $response = $adapter.get('/users');
my $created  = $adapter.post('/users', :body('name=Ada'), :headers({ Host => 'example.com' }));

request($method, $target, …) is the general form; get / post / put / patch / delete / head are shorthands. Each builds a Request, runs it through the app, and returns the Response for inspection:

1
2
3
$response.status;
$response.header('Content-Type');
$response.body;

Named arguments (:headers, :body, :scheme, :remote-address) are passed straight through to the request.

Cro adapter

MVC::Keayl::Adapter::Cro is the reference production adapter, backed by Cro:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
use MVC::Keayl::Adapter::Cro;

my $adapter = MVC::Keayl::Adapter::Cro.new(
  :app($built-app),
  :host('0.0.0.0'),
  :port(8080),
);

$adapter.start;
# ... serve ...
$adapter.stop;

For each incoming Cro request the adapter builds a Request (method, target, headers, body, and the client address), runs the app, and copies the resulting status, headers, and body onto the Cro response. Content-Length is left to Cro, which derives it from the serialized body.

The :scheme you give the adapter (http by default) tells requests whether they arrived over TLS; behind a proxy, the request's own X-Forwarded-Proto handling still applies.