The HTTP server also refered to as httpd
handles HTTP
requests as described in RFC 2616 with a few
exceptions such as gateway and proxy functionality. The same is true for servers written by NCSA and
others.
The server implements numerous features such as
SSL (Secure Sockets Layer),
ESI (Erlang Scripting Interface),
CGI (Common Gateway Interface),
User Authentication(using
Mnesia, dets or plain text database),
Common Logfile Format (with or
without disk_log(3)
support),
URL Aliasing,
Action Mappings,
Directory Listings and
SSI (Server-Side Includes).
The configuration of the server is done using Apache-style configuration directives. The goal is to be plug-in compatible with Apache.
Allmost all server functionality has been implemented using an especially crafted server API, it is described in the Erlang Web Server API. This API can be used to advantage by all who wants to enhance the server core functionality, for example custom logging and authentication.
It is possible to start a number of Web servers in an
embedded system using the services
config parameter from
an application config file. A minimal application config file
(from now on referred to as inets.config
) starting two HTTP
servers typically looks as follows:
[{inets, [{services,[{httpd,"/var/tmp/server_root/conf/8888.conf"}, {httpd,"/var/tmp/server_root/conf/8080.conf"}]}]}].
A server config file is specified for each HTTP server to be started. The config file syntax and semantics is described in the run time configuration section.
inets.config
can be tested by copying the example server
root to a specific installation directory, as described in the
run time configuration section. The example below shows a manual
start of an Erlang node, using inets.config
, and the
start of two HTTP servers listening listen on ports 8888 and
8080.
$ erl -config ./inets Erlang (BEAM) emulator version 4.9 Eshell V4.9 (abort with ^G) 1> application:start(inets). ok
All functionality in the server can be configured using
Apache-style configuration directives stored in a
configuration file. Take a look at the example config files in
the conf
directory (UNIX: $INETS_ROOT/examples/server_root/conf/, Windows: %INETS_ROOT%\examples\server_root\conf\
) of the server root for a complete
understanding.
An alphabetical list of all config directives:
Almost all server functionality has been implemented using EWSAPI (Erlang Web Server API) modules. The following modules are available:
disk_log(3)
.
Each module has a man page that further describe it's functionality.
The Modules config directive can be used to alter the server behavior, by alter the EWSAPI Module Sequence. An example module sequence can be found in the example config directory. If this needs to be altered read the EWSAPI Module Interaction section below.
The Erlang/OTP programming knowledge required to undertake an EWSAPI module is quite high and is not recommended for the average server user. It is best to only use it to add core functionality, e.g. custom authentication or a RFC 2109 implementation. |
EWSAPI should only be used to add core functionality to the server. In order to generate dynamic content, for example on-the-fly generated HTML, use the standard CGI or ESI facilities instead.
As seen above the major part of the server functionality has
been realized as EWSAPI modules (from now on only called modules).
If you intend to write your own server extension start with examining
the standard
modules (UNIX: $INETS_ROOT/src/, Windows: %INETS_ROOT%\src\
)
mod_*.erl
and note how to they are configured in the example
config
directory (UNIX: $INETS_ROOT/examples/server_root/conf/, Windows: %INETS_ROOT%\examples\server_root\conf\
).
Each module implements do/1
(mandatory), load/2
,
store/2
and remove/1
. The latter functions are needed
only when new config directives are to be introduced.
A module can choose to export functions to be used by other modules in the EWSAPI Module Sequence (See Modules config directive). This should only be done as an exception! The goal is to keep each module self-sustained thus making it easy to alter the EWSAPI Module Sequence without any unneccesary module dependencies.
A module can furthermore use data generated by previous modules in the EWSAPI Module Sequence or generate data to be used by consecutive EWSAPI modules. This is made possible due to an internal list of key-value tuples.
The server executes |
-record(mod,{data=[], socket_type=ip_comm, socket, config_db, method, absolute_uri, request_uri, http_version, request_line, parsed_header=[], entity_body, connection}).
The fields of the mod
record has the following meaning:
data
[{InteractionKey,InteractionValue}]
is used to
propagate data between modules. Depicted
interaction_data()
in function type declarations.
socket_type
socket_type()
,
Indicates whether it is a ip socket or a ssl socket.
socket
ip_comm
or ssl
format
depending on the socket_type
.
config_db
config_db()
in function type
declarations.
method
"GET" | "POST" | "HEAD" | "TRACE"
, that is the
HTTP method.
absolute_uri
"http://ServerName:Part/cgi-bin/find.pl?person=jocke"
request_uri
Request-URI
as defined
in RFC 1945, for example "/cgi-bin/find.pl?person=jocke"
http_version
HTTP
version of the
request, that is "HTTP/0.9", "HTTP/1.0", or "HTTP/1.1".
request_line
Request-Line
as
defined in RFC 1945, for example "GET
/cgi-bin/find.pl?person=jocke HTTP/1.0"
.
parsed_header
[{HeaderKey,HeaderValue}]
, parsed_header
contains
all HTTP header fields from the HTTP-request stored in a list as
key-value tuples. See RFC 2616 for a listing of all header
fields. For example the date field would be stored as:
{"date","Wed, 15 Oct 1997 14:35:17 GMT"}. RFC 2616 defines
that HTTP is a case insensitive protocol and the header fields
may be in lowercase or upper case. Httpd will ensure that all
header field names are in lowe case
.
entity_body
Entity-Body
as defined
in RFC 2616, for example data sent from a CGI-script using the
POST method.
connection
true | false
If set to true the connection to the
client is a persistent connections and will not be closed when
the request is served.
A do/1
function typically uses a restricted set of the
mod
record's fields to do its stuff and then returns a
term depending on the outcome, The outcome is either
{proceed,NewData} | {break,NewData} | done
. Which has the
following meaning:
{proceed,OldData}
OldData
refers to the data
field in the incoming mod
record.
{proceed,[{response,{StatusCode,Response}}|OldData]}
Response
) should be sent back
to the client including a status code (StatusCode
) as
defined in RFC 2616.
{proceed,[{response,{response,Head,Body}}|OldData]}
code, allow, cache_control, content_MD5, content_encoding, content_encoding, content_language, content_length, content_location, content_range, content_type, date, etag, expires, last_modified location, pragma, retry_after, server, trailer, transfer_encoding,The key code is a special case since the value to this key is a integer and not a string. The value will be used as status code for the response.
Body
is either the tuple {Fun,Arg} a
list or the atom nobody. If Body is {Fun,Arg} Fun is assumed
to be a fun that returns either close, sent or {ok,Body}. If
close is returned the connection to the client will be
closed. If sent is returned the connection to the client
will be maintained if the connection is persitent. If
{ok,Body} is returned the Body is sent back to the client as
the response body.
{proceed,[{response,{already_sent,StatusCode,Size}}|OldData]}
socket
provided by the
mod
record (see above), including a valid status code
(StatusCode
) as defined in RFC 1945 and the size
(Size
) of the response in bytes.
{proceed,[{status,{StatusCode,PhraseArgs,Reason}}}|OldData]}
StatusCode
) as
defined in RFC 1945, a term describing how the client will be
informed (PhraseArgs
) and a reason (Reason
) to why
it happened. Read more about PhraseArgs
in httpd_util:message/3.
{break,NewData}
proceed
above but with one important exception; No more
modules in the EWSAPI Module Sequence are executed. Use with
care!
done
socket
provided by the mod
record, the client will
typically get a "Document contains no data...".
Each consecutive module in the EWSAPI Module Sequence can choose to ignore data returned from the previous module either by trashing it or by "enhancing" it. |
Keep in mind that there exist numerous utility functions to help you as an EWSAPI module programmer, e.g. nifty lookup of data in ETS-tables/key-value lists and socket utilities. You are well advised to read httpd_util(3) and httpd_socket(3).
An EWSAPI module can
define new config directives thus making it configurable for a
server end-user. This is done by implementing load/2
(mandatory), store/2
and remove/1
.
The config file is scanned twice (load/2
and
store/2
) and a cleanup is done (remove/1
) during
server shutdown. The reason for this is: "A directive A can be
dependent upon another directive B which occur either before or
after directive A in the config file". If a directive
does not depend upon other directives; store/2
can be
left out. Even remove/1
can be left out if neither
load/2
nor store/2
open files or create ETS-tables
etc.
load/2
takes two arguments. The first being a row from
the config file, that is a config directive in string format
such as "Port 80"
. The second being a list of key-value
tuples (which can be empty!) defining a context. A context is
needed because there are directives which defines inner
contexts, that is directives within directives, such as <Directory>. load/2
is expected to return:
eof
ok
{ok,ContextList}
mod_auth:load/2
).
{ok,ContextList,{DirectiveKey,DirectiveValue}}
{port,80}
.
{ok,ContextList,[{DirectiveKey,DirectiveValue}]}
[{port,80},{foo,on}]
.
{error,Reason}
An example of a load function from mod_log.erl
:
load("TransferLog " ++ TransferLog, []) -> {ok, [], {transfer_log, httpd_conf:clean(TransferLog)}};
load("ErrorLog " ++ ErrorLog, []) -> {ok, [], {error_log, httpd_conf:clean(ErrorLog)}}.
store/2
takes two arguments. The first being a tuple
describing a directive ({DirectiveKey,DirectiveValue}
)
and the second argument a list of tuples describing all
directives ([{DirectiveKey,DirectiveValue}]
). This makes
it possible for directive A to be dependent upon the value of
directive B. store/2
is expected to return:
{ok,{DirectiveKey,NewDirectiveValue}}
load/2
.
{ok,[{DirectiveKey,NewDirectiveValue}]}
load/2
.
{error,Reason}
An example of a store function from mod_log.erl
:
store({error_log, ErrorLog}, ConfigList) -> case create_log(ErrorLog, ConfigList) of {ok, ErrorLogStream} -> {ok, {error_log, ErrorLogStream}}; {error, Reason} -> {error, Reason} end.
remove/1
takes the ETS-table representation of the
config-file as input. It is up to you to cleanup anything you
opened or created in load/2
or
store/2
. remove/1
is expected to return:
ok
{error,Reason}
A naive example from mod_log.erl
:
remove(ConfigDB) -> lists:foreach(fun([Stream]) -> file:close(Stream) end, ets:match(ConfigDB,{transfer_log,'$1'})), lists:foreach(fun([Stream]) -> file:close(Stream) end, ets:match(ConfigDB,{error_log,'$1'})), ok.
Modules in the EWSAPI
Module Sequence uses the mod
record's data
field to propagate responses and status messages, as seen
above. This data type can be used in a more versatile fashion. A
module can prepare data to be used by subsequent EWSAPI modules,
for example the mod_alias module
appends the tuple {real_name,string()}
to inform
subsequent modules about the actual file system location for the
current URL.
Before altering the EWSAPI Modules Sequence you are well advised to observe what types of data each module uses and propagates. Read the "EWSAPI Interaction" section for each module.
An EWSAPI module can furthermore export functions to be used by other EWSAPI modules but also for other purposes, for example mod_alias:path/3 and mod_auth:add_user/5. These functions should be described in the module documentation.
When designing an EWSAPI module try to make it self-contained, that is avoid being dependent on other modules both concerning exchange of interaction data and the use of exported functions. If you are dependent on other modules do state this clearly in the module documentation! |
You are well advised to read httpd_util(3) and httpd_conf(3).