A fast web server demonstrating some undocumented Erlang features
From Erlang Community
Contents |
Author
Sean
Overview
This HOWTO describes a web server written for the day when even Yaws is not quick enough.
The web server presented is quite simple. Even so it is split into 5 modules. Some of these are dictated by the OTP framework, and others are split out for convenience. The 5 modules are:
- iserve - API for managing URIs and callbacks
- iserve_app - OTP Application behaviour
- iserve_sup - OTP Supervisor
- iserve_server - Gen_server to own the listening socket and create connections
- iserve_socket - Process to handle a single HTTP connection for its lifetime
This HOWTO presents code and descriptions for each of these as they arise.
TCP Server Framework
A web server needs to support lots of connections, so at it's heart it needs to be a multiple connection TCP/IP server. There are any number of ways to arrange a set of erlang processes into such a thing. My favourite method is to have a single gen_server which opens and owns the listen socket (the listening process). This spawns another process which waits in accept until a connection attempt is received. At this time this accepting process sends a message back to the listening process and goes on to handle the traffic. This avoids the need for gen_tcp:controlling_process/2 and associated complexity.
On receipt of the message from the accepting process the listening process spawns a new accepting process and so on.
The listening process also traps exits, and if it receives a non normal exit from the current accepting process it creates a new one. In this way the listening process supervises its acceptor.
Common header file
The web server creates a #req{} record as it processes each request. This is used as part of the API into implementation callbacks and by the iserve_socket process. Here are the contents of iserve.hrl up front to get it out of the way:
Code listing 3.1 |
% This record characterises the connection from the browser to our server
% it is intended to be a consistent view derived from a bunch of different headers
-record(req, {connection=keep_alive, % keep_alive | close
content_length, % Integer
vsn, % {Maj,Min}
method, % 'GET'|'POST'
uri, % Truncated URI /index.html
args="", % Part of URI after ?
headers, % [{Tag, Val}]
body = |

Digg It
Del.icio.us
Reddit
Facebook
Stumble Upon
Technorati

