Skip to content

Mechanics

Marc Worrell edited this page Jul 25, 2019 · 3 revisions

How does this Cowmachine thing work, anyway?

This page describes the basic mechanics of Cowmachine from the point of view of a single incoming HTTP Request, documenting the behavior of Cowmachine through to the HTTP Response.

This is a bit different from what you might get with a "Web Framework" as we’re not going to talk about MVC, ORMs, or anything else about the rest of the shape of your application. We believe that you know better than we do how to structure your own app -- Cowmachine’s job is to help you make sure that your app’s presence on the Web is well-behaved and well-structured.

Dispatch

Dispatching is mapping an incoming HTTP request to a controller.

Cowmachine is Cowboy middleware that handles the request after dispatching the request. The dispatcher MUST set the controller option in the Env passed by the Cowboy middleware.

Optionally the dispatcher middleware before Cowmachine can also set the following Env values:

  • controller_options a proplist with options for the controller
  • site a term describing the current vhost
  • context an initialized context record without the current req (Cowmachine will set the req element)

The controller_options has one (optional) property that is interpreted by Cowmachine:

  • http_status_code an integer forcing the HTTP response status

All other option properties are ignored by Cowmachine.

The current controller and controller options can be fetched using:

cowmachine_req:controller(Context) -> module().
cowmachine_req:controller_options(Context) -> proplists:proplist().

The controller options default to the empty list [].

Error handling

If a controller function:

  • crashes, or
  • return with an http status >= 500, or
  • throws the exception {stop_request, HttpStatusCode, Reason}

then the request is redone with the controller_http_error controller.

The error controller should render a result depending on the negotiated response content type.

Decision Core

The controller then flows through the decision core, which is effectively just running the request through the HTTP flowchart. At each decision point (diamond) in the diagram, Cowmachine will determine which path to follow. In some cases it can determine the path purely from the request data -- for instance, the path from decision C3 depends purely on whether the client sent an Accept header. In many cases, however, the decision is application-specific -- the path from B10 depends on the value the controller module returns from allowed_methods. Eventually the chosen path will terminate at one of the rectangles on the diagram. At that point Cowmachine will send an appropriate HTTP response, with the headers and body dependent on the path through the diagram and the values returned by the controller’s functions.

Most of the time you don’t need to worry about this big diagram, though -- just define the controller functions relevant to your app and Cowmachine will do the rest. A good understanding of this central mechanism in Cowmachine is most useful when debugging your controllers.

From the way that Cowmachine’s decision core works, it follows that Cowmachine’s HTTP behavior is transactional. Each HTTP Request is fully received, and the resulting HTTP Response is then fully constructed before being returned. This means that while Cowmachine is suitable for a great many Web applications it is not a good fit for an application that will gradually or continuously stream responses back to clients inside the context of a single HTTP Request.

Writing Controllers

A useful way to build Cowmachine applications is often just to write a single function such as to_html to provide the most basic of stubs; when that function exists (or any other returned by content_types_provided) you can produce 200 OK responses. After that, you can easily extend your application’s Web behavior simply by filling in the other controller functions as desired.

Clone this wiki locally