YAWS_API(5) | User API | YAWS_API(5) |
yaws_api - api available to yaws web server programmers
yaws_api:Function(...)
This is the api available to yaws web server programmers. The Erlang module yaws_api contains a wide variety of functions that can be used inside yaws pages.
Each chunk of yaws code is executed while the yaws page is being delivered from the server. We give a very simple example here to show the basic idea. Imagine the following HTML code:
<html> <body> <h1> Header 1</h1> <erl> out(Arg) ->
{html, "<p> Insert this text into the document"}. </erl> </body> </html>
The out(Arg) function is supplied one argument, an #arg{} structure. We have the following relevant record definitions:
-record(arg, {
clisock, % the socket leading to the peer client
client_ip_port, % {ClientIp, ClientPort} tuple
headers, % headers
req, % request (possibly rewritten)
orig_req, % original request
clidata, % The client data (as a binary in POST requests)
server_path, % The normalized server path
% (pre-querystring part of URI)
querydata, % For URIs of the form ...?querydata
% equiv of cgi QUERY_STRING
appmoddata, % (deprecated - use pathinfo instead) the remainder
% of the path leading up to the query
docroot, % Physical base location of data for this request
docroot_mount, % virtual directory e.g /myapp/ that the docroot
% refers to.
fullpath, % full deep path to yaws file
cont, % Continuation for chunked multipart uploads
state, % State for use by users of the out/1 callback
pid, % pid of the yaws worker process
opaque, % useful to pass static data
appmod_prepath, % (deprecated - use prepath instead) path in front
% of: <appmod><appmoddata>
prepath, % Path prior to 'dynamic' segment of URI.
% ie http://some.host/<prepath>/<script-point>/d/e
% where <script-point> is an appmod mount point,
% or .yaws,.php,.cgi,.fcgi etc script file.
pathinfo, % Set to '/d/e' when calling c.yaws for the request
% http://some.host/a/b/c.yaws/d/e
% equiv of cgi PATH_INFO
appmod_name % name of the appmod handling a request,
% or undefined if not applicable
}).
The headers argument is also a record:
-record(headers, {
connection,
accept,
host,
if_modified_since,
if_match,
if_none_match,
if_range,
if_unmodified_since,
range,
referer,
user_agent,
accept_ranges,
cookie = [],
keep_alive,
location,
content_length,
content_type,
content_encoding,
authorization,
transfer_encoding,
x_forwarded_for,
other = [] % misc other headers
}).
The out/1 function can use the Arg to generate any content it likes. We have the following functions to aid that generation.
{expires, UtcTime} - Cookie expiration time, where UtcTime is
a tuple returned by calendar:universal_time/0. {max_age, Age} - Defines the lifetime of the cookie, in seconds,
where age is an integer >= 0. {path, Path} - Path is a string that specifies the subset of URLs to
which this cookie applies. {domain, Domain} - Domain is a string that specifies the domain for which
the cookie is valid. {same_site, Policy} - Policy is one of the atoms lax, none or strict. {comment, Comment} - Comment is a string that doccuments the server's
intended use of the cookie. secure - Directs the user agent to use only secure means to
contact the origin server whenever it sends back this
cookie. http_only - Restricts cookie access from other non-HTTP APIs.
The function returns [] if no cookie was found, otherwise the actual cookie is returned as a string.
#setcookie{} record is defined in yaws_api.hrl:
-record(setcookie, {key,
value,
quoted = false,
domain,
max_age,
expires,
path,
secure = false,
http_only = false,
extensions = []}).
#cookie{} record is defined in yaws_api.hrl:
-record(cookie, {key,
value,
quoted = false}).
out(Arg) ->
... do some stuff
Ret = [{redirect, "http://www.somewhere.com"},
setcookie("sid", Random)
].
-record(redir_self, {
host, % string() - our own host
scheme, % http | https
scheme_str, % "https://" | "http://"
port, % integer() - our own port
port_str % "" | ":<int>" - the optional port part
% to append to the url
}).
{line, Line, Tail} or {lastline, Line, Tail}
The function handles multilines as defined in e.g. SMTP or HTTP
If the function returns {result, Res} no more data will come from the browser.
If the function returns {cont, Cont, Res} the browser will supply more data. (The file was too big to come in one read)
This indicates that there is more data to come and the out/1 function should return {get_more, Cont, User_state} where User_state might usefully be a File Descriptor. The Res value is a list of either: {head, {Name, Headers}} | {part_body, Binary} | {body, Binary}
The function returns {error, Reason} when an error occurred during the parsing.
Example usage could be:
<erl>
out(A) ->
case yaws_api:parse_multipart_post(A) of
{cont, Cont, Res} ->
St = handle_res(A, Res),
{get_more, Cont, St};
{result, Res} ->
handle_res(A, Res),
{html, f("<pre>Done </pre>",[])};
{error, Reason} ->
{html, f("An error occured: ~p", [Reason])}
end.
handle_res(A, [{head, {Name, _Hdrs}}|T]) ->
io:format("head:~p~n",[Name]),
handle_res(A, T);
handle_res(A, [{part_body, Data}|T]) ->
io:format("part_body:~p~n",[Data]),
handle_res(A, T);
handle_res(A, [{body, Data}|T]) ->
io:format("body:~p~n",[Data]),
handle_res(A, T);
handle_res(A, []) ->
io:format("End_res~n").
</erl>
Options = [Option] Option -- one of the following:
{app_server_host, string() | ip_address()} The hostname or the IP address of the FastCGI application server.
{app_server_port, 0..65535} The TCP port number of the FastCGI application server.
{path_info, string()} Override default pathinfo in Arg#arg.pathinfo.
{extra_env, ExtraEnv} Extra environment variables to be passed to the FastCGI application server, as a list of name-value pairs.
ExtraEnv = [Var] Var = {Name, Value} Name = string() | binary() Value = string() | binary()
{trace_protocol, boolean()} Enable or disable tracing of FastCGI protocol messages as info log messages.
{log_app_error, boolean()} Enable or disable logging of application error messages: output to stderr and non-zero exit value.
If access is denied, Out contains the complete response returned by the FastCGI application server. This response is typically returned as-is to the HTTP client.
If access is allowed, Out contains the response returned by the FastCGI application server minus the body (i.e. minus the content) which should be ignored per the FastCGI specification. This response is typically not returned to the HTTP client. The calling application module may wish to inspect the response, for example by extracting variables (see fcgi_extract_variables below) or by inspecting the headers returned by the FastCGI application server.
Out -- See return values for out/1 below
Name = string() -- The name of the variable (the "Variable-" prefix has already been removed). Value = string() -- The value of the variable.
The out/1 function can return different values to control the behavior of the server.
EHTML = [EHTML] | {Tag, Attrs, Body} | {Tag, Attrs} | {Tag} |
{Module, Fun, [Args]} | fun/0 |
binary() | character() Tag = atom() Attrs = [{Key, Value}] Key = atom() Value = string() | binary() | atom() | integer() | float() |
{Module, Fun, [Args]} | fun/0 Body = EHTML
For example, {p, [], "Howdy"} expands into "<p>Howdy</p>" and
{form, [{action, "a.yaws"}],
{input, [{type,text}]}}
expands into
<form action="a.yaws"
<input type="text"> </form>
It may be more convenient to generate erlang tuples than plain html code.
However it makes it possible to stream data to the client if the yaws code doesn't have access to all the data in one go. (Typically if a file is very large or if data arrives from back end servers on the network.
However it makes it possible to stream data to the client directly from an application process to the socket. This approach can be useful for applications that employ long-polling (Comet) techniques, for example, and for applications wanting to avoid buffering data or avoid HTTP chunked mode transfer for streamed data.
However it makes it possible to stream data to the client by setting the content length of the response. As the opposite of other ways to stream data, in this case, the response is not chunked encoded.
{connection, What}
This sets the Connection: header. If What is the special value "close", the connection will be closed once the yaws page is delivered to the client.
{server, What}
Sets the Server: header. By setting this header, the server's signature will be dynamically overloaded.
{location, Url}
Sets the Location: header. This header is typically combined with the {status, 302} return value.
{cache_control, What}
Sets the Cache-Control: header.
{expires, What}
Sets the Expires: header.
{date, What}
Sets the Date: header.
{allow, What}
Sets the Allow: header.
{last_modified, What}
Sets the Last-Modified: header.
{etag, What}
Sets the Etag: header.
{set_cookie, Cookie}
Prepends a Set-Cookie: header to the list of previously set Set-Cookie: headers.
{content_range, What}
Sets the Content-Range: header.
{content_type, MimeType}
Sets the Content-Type: header.
{content_encoding, What}
Sets the Content-Encoding: header. If this header is defined, no deflate is performed by Yaws, allowing you to compress data yourself if you wish to do so.
{content_length, Len}
Normally yaws will ship Yaws pages using Transfer-Encoding: chunked. This is because we generally can't know how long a yaws page will be. If we for some reason want to force a Content-Length: header (and we actually do know the length of the content, we can force Yaws to not ship the page chunked.
{transfer_encoding, What}
Sets the Transfer-Encoding: header.
{www_authenticate, What}
Sets the WWW-Authenticate: header.
{vary, What}
Sets the Vary: header.
{accept_ranges, What}
Sets the Accept-Ranges: header.
All other headers must be added using the normal HTTP syntax. Example:
{header, {"My-X-Header", "gadong"}} or {header, "My-X-Header: gadong"}
Make Yaws returns a different page than the one being requested. Page is a Request-URI, so it must be url-encoded and can contain a query-string.
{status, C} - Set the HTTP response status code C for page Page.
{header, H} - Accumulate the HTTP header H for page Page.
{disable_cache, Bool} - if set to true, disable the cache of Page for this call.
Example: Delimiter = %%
File contains the string .... %%xyz%% .....
Bindings contain the tuple {"xyz", "Dingbat"}
The occurrence of %%xyz%% in File will be replaced with "Dingbat" in the Server side included output.
The {ssi, File, Delimiter, Bindings} statement can also occur within a deep ehtml structure.
The special directive strip_undefined can be specified in the Bindings list, just as it can for the {bindings, ....} directive, but it's ignored because treating undefined variables as empty is the default for ssi bindings.
All bindings can then be used in the rest of yaws code (in HTML source and within erl tags). In HTML source %%Key%% is expanded to Value and within erl tags yaws_api:binding(Key) (which calls error if no such binding exists) or yaws_api:binding_find(Key) (which returns undefined if no such binding exists) can be used to extract Value, and yaws_api:binding_exists(Key) can be used to check for the existence of a binding.
If a page happens to contains text that looks like a binding, e.g. %%SomeText%%, but no key SomeText is supplied, then by default the original text is left as is. If you prefer that anything parsed as a binding gets stripped out of a page whenever the bindings directive does not specify its key, include the special directive strip_undefined in the bindings list:
{bindings, [{Key1, Value1}, strip_undefined]}
Written by Claes Wikstrom