[Ericsson AB]

mod_esi

MODULE

mod_esi

MODULE SUMMARY

Efficient Erlang Scripting

DESCRIPTION

The Erlang Scripting Interface (ESI) provides a tight and efficient interface to the execution of Erlang functions. Erlang functions can be executed with two alternative schemes, eval and erl. Both of these schemes can utilize the functionality in an Erlang node efficiently.

Even though the server supports CGI-1.1 the use of the Erlang Scripting Interface (ESI) is encouraged for reasons of efficiency. CGI is resource intensive because of it's design. CGI requires the server to fork a new OS process for each executable it needs to start.

An Erlang function can be written and executed as a CGI script by using erl_call(3) in the erl_interface library, for example. The cost is a forked OS process, as described above. This is a waste of resources, at least when the Web server itself is written in Erlang (as in this case).

The following config directives are described:

ERL SCHEME

The erl scheme is designed to mimic plain CGI, but without the extra overhead. An URL which calls an Erlang erl function has the following syntax (regular expression):

http://your.server.org/***/Mod[:/]Func(?QueryString|/PathInfo)
    

The module (Mod) referred to must be found in the code path, and it must define a function (Func) with an arity of two or three i.e. Func(Env,Input) or Func(SessionID,Env,Input). Env contains information about the connecting client (see below), and Input the QueryString or PathInfo as defined in the CGI specification. SessionID is a identifier that is used to send parts of the web page back to the user through the function mod_esi:deliver/2

*** above depends on how the ErlScriptAlias config directive has been used. Data returned from the function with arity of two must furthermore take the form as specified in the CGI specification.

It is preferable to use the callback function with an arity of three, since the function can send the data back to the clients in parts instead of generating the whole page before it is sent. The Web server sends the data back to HTTP/1.1 compliant clients with chunked encoding this means that the Content-Length header field is not necessary, and should indeed be avoided.

mod_esi assumes that if the first chunk of data delivered to the client through the function mod_esi:deliver/2 contains all HTTP-header fields the script will generate. I the first chunk does not contain the string "\r\n\r\n"c mod_esi assumes that the script not will generate any header data.

Take a look at httpd_example.erl in the code release (UNIX: $INETS/src, Windows: %INETS\src) for a clarifying example. Start an example server as described in httpd:start/0 and test the following from a browser (The server name for your example server will differ!):

http://your.server.org:8888/cgi-bin/erl/httpd_example/newformat
and a call will be made to httpd_example:newformat/3 Something like this will promptly be shown in the browser:
          This new format is nice.
          This new format is nice.
          This new format is nice.
        
http://your.server.org:8888/cgi-bin/erl/httpd_example/get
and a call will be made to httpd_example:get/2 and two input fields and a Submit button will promptly be shown in the browser. Enter text into the input fields and click on the Submit button. Something like this will promptly be shown in the browser:
Environment:
[{query_string,"input1=blaha&input2=blaha"},
 {server_software,"eddie/2.2"},
 {server_name,"localhost"},
 {gateway_interface,"CGI/1.1"},
 {server_protocol,"HTTP/1.0"},
 {server_port,8080},
 {request_method,"GET"},
 {remote_addr,"127.0.0.1"},
 {script_name,"/cgi-bin/erl/httpd_example:get?input1=blaha&
                                              input2=blaha"},
 {http_accept_charset,"iso-8859-1,*,utf-8"},
 {http_accept_language,"en"},
 {http_accept,"image/gif, image/x-xbitmap, image/jpeg, 
                                           image/pjpeg, */*"},
 {http_host,"localhost:8080"},
 {http_user_agent, "Mozilla/4.03 [en] (X11; 
                                       I; Linux 2.0.30 i586)"},
 {http_connection,"Keep-Alive"}, 
 {http_referer,
  "http://localhost:8080/cgi-­bin/erl/
         httpd_example/get"}]
Input:
 input1=blaha&input2=blaha
Parsed Input:
 [{"input1","blaha"},{"input2","blaha"}] 
        
http://your.server.org:8888/cgi-bin/erl/httpd_example:post
A call will be made to httpd_example:post/2. The same thing will happen as in the example above but the HTTP POST method will be used instead of the HTTP GET method.
http://your.server.org:8888/cgi-bin/erl/httpd_example:yahoo
A call will be made to to httpd_example:yahoo/2 and the Yahoo! site will promptly be shown in your browser.

Note!

httpd:parse_query/1 is used to generate the Parsed Input: ... data in the example above.

If a client closes the connection prematurely a message will be sent to the function, that is either {tcp_closed,_} or {_,{socket_closed,_}}.

EVAL SCHEME

Warning!

The eval scheme can seriously threaten the integrity of the Erlang node housing a Web server, for example:

http://your.server.org/eval?httpd_example:
       print(atom_to_list(apply(erlang,halt,[])))
      

which effectively will close down the Erlang node, that is use the erl scheme instead until this security breach has been fixed.

Today there are no good way of solving this problem and therefore Eval Scheme may be removed in future release-s of Inets.

The eval scheme is straight-forward and does not mimic the behavior of plain CGI. An URL which calls an Erlang eval function has the following syntax:

http://your.server.org/***/Mod:Func(Arg1,...,ArgN)
    

The module (Mod) referred to must be found in the code path, and data returned by the function (Func) is passed back to the client. *** depends on how the EvalScriptAlias config directive has been used. Data returned from the function must furthermore take the form as specified in the CGI specification.

Take a look at httpd_example.erl in the code release (UNIX: $INETS/src, Windows: %INETS\src) for an example. Start an example server as described in httpd:start/0 and test the following from a browser (The server name for your example server will differ!):

http://your.server.org:8888/eval?httpd_example:print("Hi!")
and a call will be made to httpd_example:print/1 and "Hi!" will promptly be shown in your browser.

DIRECTIVE: "ErlScriptAlias"

Syntax: ErlScriptAlias url-path allowed-module allowed-module ...
Default: - None -
Module: mod_esi(3)

ErlScriptAlias marks all URLs matching url-path as erl scheme scripts. A matching URL is mapped into a specific module and function. The module must be one of the allowed-module:s. For example:

ErlScriptAlias /cgi-bin/hit_me httpd_example md4
    

and a request to http://your.server.org/cgi-bin/hit_me/httpd_example:yahoo would refer to httpd_example:yahoo/2. Refer to the Erl Scheme description above.

DIRECTIVE: "ErlScriptNoCache"

Syntax: ErlScriptNoCache true | false
Default: false
Module: mod_esi(3)

If ErlScriptNoCache is set to true the server will add http header fields that prevents proxies from caching the page. This is generally a good idea for dynamic content, since the content often vary between each request.

      ErlScriptNoCache true
    

DIRECTIVE: "ErlScriptTimeout"

Syntax: ErlScriptTimeout seconds
Default: 15
Module: mod_esi(3)

If ErlScriptTimeout sets the time in seconds the server will wait between each chunk of data is delivered through mod_esi:deliver/2 when the new Erl Scheme format, that takes three argument is used.

      ErlScriptTimeout 15
    

DIRECTIVE: "EvalScriptAlias"

Syntax: EvalScriptAlias url-path allowed-module allowed-module ...
Default: - None -
Module: mod_esi(3)

EvalScriptAlias marks all URLs matching url-path as eval scheme scripts. A matching URL is mapped into a specific module and function. The module must be one of the allowed-module:s. For example:

EvalScriptAlias /cgi-bin/hit_me_to httpd_example md5
    

and a request to http://your.server.org/cgi-bin/hit_me_to/httpd_example:print("Hi!") would refer to httpd_example:print/1. Refer to the Eval Scheme description above.

EWSAPI MODULE INTERACTION

Uses the following EWSAPI interaction data, if available:

{remote_user,RemoteUser}
as defined in mod_auth(3).

Exports the following EWSAPI interaction data, if possible:

{mime_type,MimeType}
The file suffix of the incoming URL mapped into a MimeType as defined in the Mime Type Settings section of httpd_core(3).

Uses the following EWSAPI functions:

EXPORTS

deliver(SessionID, Data) -> ok | {error,Reason}

Types:

SessionID = term()
Data = string()
Reason = term()

This function is only intended to be used from functions called by the Erl Scheme interface to deliver parts of the content to the user.

Sends data from a Erl Scheme script back to the client. Note that if any HTTP-header fields will be sent back to the client they must be in the first call to deliver/2. Do not assume anything about the data type of SessionID, the SessionID must be the SessionID from the Erl Scheme call.

ESWAPI CALLBACK FUNCTIONS

EXPORTS

Module:Function(Env, Input)-> Response

Types:


Env = [EnvironmentDirectives] ++ ParsedHeader
EnvironmentDirectives = {Key,Value}
Key = query_string | content_length, server_software, gateway_interface, server_protocol, server_port, request_method, remote_addr, script_name. <v>Input = Response = string()

The Module must be found in the code path and export Function with an arity of two. An erlScriptAlias must also be set up in the configuration file for the Web server.

If the HTTP request is a post request and a body is sended then content_length will be the length of the posted data. If get is used query string will be the data after ? in the url.

ParsedHeader is the HTTP request as a key value tuple list. The keys in parsed header will be the in lower case.

This callback format consumes quite much memory since the whole response must be generated before it is sent to the user. Therefore it is better to use the callback function with an arity of three.

Module:Function(SessionID, Env, Input)-> void

Types:

SessionID = term()
Env = [EnvironmentDirectives] ++ ParsedHeader
EnvironmentDirectives = {Key,Value}
Key = query_string | content_length, server_software, gateway_interface, server_protocol, server_port, request_method, remote_addr, script_name. <v>Input = Response = string()

For information about Environment and Input see Module:Function/2 above. SessionID is a identifier the server use when deliver/2 is called, do not assume any-thing about the datatype.

Use this callback function to dynamicly generate dynamic web content. when a part of the page is generated send the data back to the client through deliver/2. Note that the first chunk of data sent to the client must at least contain all HTTP header fields that the response will generate. If the first chunk not contains End of HTTP header that is "\r\n\r\n" the server will assume that no HTTP header fields will be generated.

SEE ALSO

httpd(3), mod_alias(3), mod_auth(3), mod_security(3), mod_cgi(3)

AUTHORS

Joakim Grebenö - support@erlang.ericsson.se

inets 3.0.9
Copyright © 1991-2006 Ericsson AB