Serve PHP applications with Erlang and Cowboy using FastCGI

Introduction

In the last article, we discussed how to use erl_fastcgi to call FastCGI applications from Erlang, and today we're going to see how we can do this from a Cowboy application in order to serve PHP pages from Erlang.

Requirements

You will need PHP installed and a wokring php-fpm installation.

The code

The code for this example is available at GitHub at https://github.com/marcelog/cowboy_fastcgi and it's actually a very small and simple Erlang/OTP application. It has the following files:

cowboy_fastcgi_app.erl: The application itself, will start the main supervisor.

cowboy_fastcgi_sup.erl: The main supervisor will setup the cowboy routes and also create a pool of erl_fastcgi workers using poolboy.

cowboy_fastcgi_cfg.erl: Just a helper module used to get different configuration options.

cowboy_fastcgi_handler.erl: The interesting stuff goes here, and we'll take a closer look now. This is the endpoint handler in charge of using erl_fastcgi to call the php-fpm server.

Handling the request and serving the FastCGI result from php-fpm

As you can see in the init/2 function in cowboy_fastcgi_handler.erl file, the first thing we do is to gather some standard information from the request and create the set of FastCGI Parameters that php-fpm needs in order to serve the request (and also makes them available to our PHP scripts through different global variables). This is done here.

Then, the FastCGI request is done and we have to wait for the incoming messages with different signaling information from erl_fastcgi, we do this here. As you can see, several messages are handled:

fast_cgi_stdout : This is basically the most important message. The stdout of the script will include the HTTP status, headers, and body to be returned.

: This is basically the most important message. The stdout of the script will include the HTTP status, headers, and body to be returned. fast_cgi_stderr : There was an error processing the request, so we log the stderr output of the script and wait for more messages that can be sent to the browser.

: There was an error processing the request, so we log the stderr output of the script and wait for more messages that can be sent to the browser. fast_cgi_request_done : The request was aborted, so we return a status 500 and some error message.

: The request was aborted, so we return a status 500 and some error message. fast_cgi_done: The request finished successfully, so we have nothing else to do but finish the http request in turn.

Parsing the FastCGI status, headers and body

Each fast_cgi_stdout message will contain up to 65535 bytes of data, this means that status, headers, and body may be in the same line or split into many, so we have to parse them and return them accordingly to the browser. This is done here

Here's an example of php-fpm returning a 404 due to an invalid script name:

And here's an example of a successful run:

First we split the line looking for the \r

\r

that separates the headers from the body. Once that's done, the headers are split by \r

and then each one of those split by :. Once we have all the headers, we look for the status header (it's not a header actually but it has the same format) and then everything is returned to the client here and by using cowboy_req:stream_reply and coboy_req:stream_body.

And voilà, we're now ready to extend our Erlang/OTP web application with PHP scripts and include other devs in our proyect :)