Skip to content

Commit

Permalink
inets: Polish mod_esi documentation
Browse files Browse the repository at this point in the history
This commit:

- Moves the data type documentation for `env()` directly to the data
  type itself
- Introduces the opaque `session_id()` data type
- Adds a summary header for the callback and `deliver` functions
- Removed the redundant "ESI Callback Functions" part of the "Callbacks"
  header
- Groups and reword the callback docs to be more readable.
  • Loading branch information
jchristgit committed Feb 23, 2025
1 parent 19391bb commit a13f9f9
Showing 1 changed file with 94 additions and 71 deletions.
165 changes: 94 additions & 71 deletions lib/inets/src/http_server/mod_esi.erl
Original file line number Diff line number Diff line change
Expand Up @@ -25,43 +25,7 @@ Erlang Server Interface
This module defines the Erlang Server Interface (ESI) API. It is a more
efficient way of writing Erlang scripts for your `Inets` web server than writing
them as common CGI scripts.
### Data types
The following data types are used in the functions for mod_esi:
- **`env() =`** - `{EnvKey()::atom(), Value::term()}`
Currently supported key value pairs
- **`{server_software, string()}`** - Indicates the inets version.
- **`{server_name, string()}`** - The local hostname.
- **`{gateway_interface, string()}`** - Legacy string used in CGI, just
ignore.
- **`{server_protocol, string()}`** - HTTP version, currently "HTTP/1.1"
- **`{server_port, integer()}`** - Servers port number.
- **`{request_method, "GET" | "PUT" | "DELETE" | "POST" | "PATCH"}`** - HTTP
request method.
- **`{remote_adress, inet:ip_address()}`** - The clients ip address.
- **`{peer_cert, undefined | no_peercert | DER:binary()}`** - For TLS
connections where client certificates are used this will be an ASN.1
DER-encoded X509-certificate as an Erlang binary. If client certificates are
not used the value will be `no_peercert`, and if TLS is not used (HTTP or
connection is lost due to network failure) the value will be `undefined`.
- **`{script_name, string()}`** - Request URI
- **`{http_LowerCaseHTTPHeaderName, string()}`** - example:
\{http_content_type, "text/html"\}
""".
-moduledoc(#{titles => [{callback,<<"ESI Callback Functions">>}]}).

%% API
%% Functions provided to help erl scheme alias programmer to
Expand All @@ -72,6 +36,8 @@ The following data types are used in the functions for mod_esi:
%% Callback API
-export([do/1, store/2]).

-export_type([session_id/0]).

-include("httpd.hrl").
-include("httpd_internal.hrl").
-include_lib("kernel/include/logger.hrl").
Expand All @@ -85,6 +51,38 @@ The following data types are used in the functions for mod_esi:
%%%=========================================================================
%%% Types
%%%=========================================================================
-doc """
Environment data associated with a request.
## Possible values
- **`{server_software, string()}`** - Indicates the inets version.
- **`{server_name, string()}`** - The local hostname.
- **`{gateway_interface, string()}`** - Legacy string used in CGI, just
ignore.
- **`{server_protocol, string()}`** - HTTP version, currently "HTTP/1.1"
- **`{server_port, integer()}`** - Servers port number.
- **`{request_method, "GET" | "PUT" | "DELETE" | "POST" | "PATCH"}`** - HTTP
request method.
- **`{remote_adress, inet:ip_address()}`** - The clients ip address.
- **`{peer_cert, undefined | no_peercert | DER:binary()}`** - For TLS
connections where client certificates are used this will be an ASN.1
DER-encoded X509-certificate as an Erlang binary. If client certificates are
not used the value will be `no_peercert`, and if TLS is not used (HTTP or
connection is lost due to network failure) the value will be `undefined`.
- **`{script_name, string()}`** - Request URI
- **`{http_LowerCaseHTTPHeaderName, string()}`** - example:
`{http_content_type, "text/html"}`
""".
-type env() :: {server_software, string()} |
{server_name, string()} |
{gateway_interface, string()} |
Expand All @@ -96,57 +94,82 @@ The following data types are used in the functions for mod_esi:
{script_name, string()} |
{http_LowerCaseHTTPHeaderName, string()}.


-doc """
Identifies the requesting client.
""".
-opaque session_id() :: term().

%%%=========================================================================
%%% Callbacks
%%%=========================================================================
-doc """
Called by `mod_esi` in response to requests.
`Module` must be found in the code path and export `Function` with an arity of
three. An `erlScriptAlias` must also be set up in the configuration file for the
web server.
three. An `erl_script_alias` must also be set up in the configuration file for
the web server, see [the ESI properties documentation](`m:httpd#prop_esi_alias`).
`mod_esi:deliver/2` shall be used to generate the response to the client and
`SessionID` is an identifier that shall by used when calling this function, do
not assume anything about the datatype. This function may be called several
times to chunk the response data. Notice 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 does not contain the _end of HTTP header_, that is,
`"\r\n\r\n",` the server assumes that no HTTP header fields will be generated.
The `Module` and `Function` that are called depend on the URL. See [the ESI
introductory documentation](http_server.md#esi) for more details.
To set the response status code, the special `status` response header can be
sent. For instance, to acknowledge creation of a resource and annotate the
response content type with JSON, one could respond with the following headers:
`mod_esi:deliver/2` shall be used to generate the response to the client, and
`SessionID` shall be passed as the first argument.
```erlang
"status: 201 Created\r\n content-type: application/json\r\n\r\n"
```
## Chunking
`Env` environment data of the request, see description above.
This function may be called several times to chunk the response data. Notice
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 does not
contain the _end of HTTP header_, that is, `"\r\n\r\n"`, the server assumes
that no HTTP header fields will be generated. This behaviour depends on the
`httpd` configuration, see below.
`Input` is query data of a GET request or the body of a PUT or POST request. The
default behavior (legacy reasons) for delivering the body, is that the whole
body is gathered and converted to a string. But if the httpd config parameter
[max_client_body_chunk](`m:httpd#max_client_body_chunk`) is set, the body will
be delivered as binary chunks instead. The maximum size of the chunks is either
[max_client_body_chunk](`m:httpd#max_client_body_chunk`) or decide by the client
if it uses HTTP chunked encoding to send the body. When using the chunking
mechanism this callback must return \{continue, State::term()\} for all calls
where `Input` is `{first, Data::binary()}` or
`{continue, Data::binary(), State::term()}`. When `Input` is
`{last, Data::binary(), State::term()}` the return value will be ignored.
## Parameters
- `SessionID`: request identifier.
Pass this to `mod_esi:deliver/2` when generating a response.
- `Env`: environment data of the request, see `t:env/0`.
- `Input`: query data of a GET request or the body of a PUT or POST request.
The default behavior (legacy reasons) for delivering the body, is that the
whole body is gathered and converted to a string. But if the httpd config
parameter [`max_client_body_chunk`](`m:httpd#max_client_body_chunk`) is set,
the body will be delivered as binary chunks instead. The maximum size of the
chunks is either [`max_client_body_chunk`](`m:httpd#max_client_body_chunk`) or
decided by the client if it uses HTTP chunked encoding to send the body.
When using the chunking mechanism, this callback must return `{continue,
State::term()}` for all calls where `Input` is `{first, Data::binary()}` or
`{continue, Data::binary(), State::term()}`. When `Input` is `{last,
Data::binary(), State::term()}` the return value will be ignored.
The input `State` is the last returned `State`, in it the callback can include
any data that it needs to keep track of when handling the chunks.
> #### Note {: .info }
>
> Note that if the body is small all data may be delivered in only one chunk and
> then the callback will be called with \{last, Data::binary(), undefined\}
> then the callback will be called with `{last, Data::binary(), undefined}`
> without getting called with `{first, Data::binary()}`.
The input `State` is the last returned `State`, in it the callback can include
any data that it needs to keep track of when handling the chunks.
## Setting a response status
To set the response status code, the special `status` response header can be
sent. For instance, to acknowledge creation of a resource and send an empty
JSON response body, one could pass the following:
```erlang
"status: 201 Created\r\ncontent-type: application/json\r\n\r\n{}"
```
""".
-doc(#{title => <<"ESI Callback Functions">>}).
-callback 'Function'(SessionID, Env, Input) -> {continue, State} | _
when
SessionID :: term(),
SessionID :: session_id(),
Env :: [env()],
Input :: string() | ChunkedData,
ChunkedData ::
Expand Down Expand Up @@ -176,10 +199,10 @@ any data that it needs to keep track of when handling the chunks.
%% request handling process so it can forward it to the client.
%%-------------------------------------------------------------------------
-doc """
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 an ESI script back to the client.
Sends data from an Erl Scheme script back to the client.
This function is _only_ intended to be used from functions called by the ESI
interface to deliver parts of the content to the user.
> #### Note {: .info }
>
Expand All @@ -191,7 +214,7 @@ Sends data from an Erl Scheme script back to the client.
> you implemented.
""".
-spec deliver(SessionID, Data) -> ok | {error, Reason} when
SessionID :: term(),
SessionID :: session_id(),
Data :: iolist(),
Reason :: bad_sessionID.

Expand Down

0 comments on commit a13f9f9

Please sign in to comment.