Skip to content

Instantly share code, notes, and snippets.

@jberger
Last active June 6, 2017 14:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jberger/102d2deb43bf325185c954afe18d2fa0 to your computer and use it in GitHub Desktop.
Save jberger/102d2deb43bf325185c954afe18d2fa0 to your computer and use it in GitHub Desktop.

(Note in this document, I am very careful about using the name Mojo vs Mojolicious, and also that this only deals with simple HTTP requests).

When a server is instantiated, it subscribes to its own request event with a callback which calls the app's handler method (which I shall discuss later).

When the server starts listening, it establishes a stream read handler. When that fires, if necessary it builds a transaction which subscribes to its own request method which will then emit a request event from the server (which we already subscribed to above) and then consumes data/text from the stream.

When the transaction believes that the message is complete it emits a request event which as we saw is re-emitted from the server thereby firing the app's handler method. As defined in the Mojo docs, the handler method "is the main entry point to your application or framework and will be called for each new transaction". Sidenote, to implement a new framework on Mojo, all you need to do is inherit from Mojo and implement that method.

Your Mojolicious-based app usually doesn't implement the handler method; usually it is inherited from the Mojolicious class. Before that is fired, let's first look at the constructor that Mojolicious implements. After setting up some defaults, the new method subscribes to the around_dispatch hook for exception handling (and does so before you have a chance to subscribe, it wants to be first) and then calls your startup method. In this method you establish your application, most notably its routes.

Now back to the handler, Mojo's handler method is meant to be overloaded and so Mojolicious implements it. Looking within, you see how the application subscribes to its own around_action and around_dispatch hooks on (and only on) the first request. Note that this time, by doing so on the first request (and thus after the startup method) these are the last subscribers to these hooks.

Moving on, for each request, the application builds a controller for the transaction and emits the around_dispatch hook (which was just established if this is the first request). If the request reaches the end of the hook subscribers without setting mojo.rendered then it logs that nothing has been rendered.

If your startup established an around_dispatch hook, then it is fired, however the last (and usually only) subscriber is the application's own (as seen above). When this fires, it calls the app's dispatch method. This method emits the before_dispatch hook, handles static files, then emits the before_routes hook and lets the router handle most the remaining dispatch. The router is beyond the scope of this document.

Once the request is routed, there are many ways that the response can be rendered, but mainly the reader should focus on the controller's render and rendered methods. Eventually the data accumulated via routing and rendering is converted back into a message which is written to the stream by telling the transaction to resume which causes a the transaction to emit a resume event. Now back when the Server initially emitted the request event, it also established a subscriber for the resume event. That callback finally writes the response to the stream.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment