This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
yap-6.3/packages/http/http.doc
2010-06-23 11:52:34 +01:00

1838 lines
68 KiB
Plaintext

\documentclass[11pt]{article}
\usepackage{times}
\usepackage{pl}
\usepackage{plpage}
\usepackage{html}
\makeindex
\onefile
\htmloutput{html} % Output directory
\htmlmainfile{index} % Main document file
\bodycolor{white} % Page colour
\sloppy
\renewcommand{\runningtitle}{SWI-Prolog HTTP support}
\begin{document}
\title{SWI-Prolog HTTP support}
\author{Jan Wielemaker \\
HCS, \\
University of Amsterdam \\
The Netherlands \\
E-mail: \email{J.Wielemaker@uva.nl}}
\maketitle
\begin{abstract}
This article documents the package HTTP, a series of libraries for
accessing data on HTTP servers as well as providing HTTP server
capabilities from SWI-Prolog. Both server and client are modular
libraries. The server can be operated from the Unix \program{inetd}
super-daemon as well as as a stand-alone server that runs on all
platforms supported by SWI-Prolog.
\end{abstract}
\vfill
\pagebreak
\tableofcontents
\vfill
\vfill
\newpage
\section{Introduction}
The HTTP (HyperText Transfer Protocol) is the W3C standard protocol for
transferring information between a web-client (browser) and a
web-server. The protocol is a simple \emph{envelope} protocol where
standard name/value pairs in the header are used to split the stream
into messages and communicate about the connection-status. Many
languages have client and or server libraries to deal with the HTTP
protocol, making it a suitable candidate for general purpose
client-server applications.
In this document we describe a modular infra-structure to access
web-servers from SWI-Prolog and turn Prolog into a web-server.
\subsection*{Acknowledgements}
This work has been carried out under the following projects:
\url[GARP]{http://hcs.science.uva.nl/projects/GARP/},
\url[MIA]{http://www.ins.cwi.nl/projects/MIA/},
\url[IBROW]{http://hcs.science.uva.nl/projects/ibrow/home.html},
\url[KITS]{http://kits.edte.utwente.nl/} and
\url[MultiMediaN]{http://e-culture.multimedian.nl/}
The following people have pioneered parts of this library and
contributed with bug-report and suggestions for improvements: Anjo
Anjewierden, Bert Bredeweg, Wouter Jansweijer, Bob Wielinga, Jacco
van Ossenbruggen, Michiel Hildebrandt, Matt Lilley and Keri Harris.
\section{The HTTP client libraries}
This package provides two packages for building HTTP clients. The first,
\pllib{http/http_open} is a lightweight library for opening a HTTP
URL address as a Prolog stream. It can only deal with the HTTP GET
protocol. The second, \pllib{http/http_client} is a more advanced
library dealing with \jargon{keep-alive}, \jargon{chunked transfer} and
a plug-in mechanism providing conversions based on the MIME content-type.
\input{httpopen.tex}
\subsection{The \pllib{http/http_client} library} \label{sec:httpclient}
The \pllib{http/http_client} library provides more powerful access to
reading HTTP resources, providing \jargon{keep-alive} connections,
\jargon{chunked} transfer and conversion of the content, such as
breaking down \jargon{multipart} data, parsing HTML, etc. The library
announces itself as providing \const{HTTP/1.1}.
\begin{description}
\predicate{http_get}{3}{+URL, -Reply, +Options}
Performs a HTTP GET request on the given URL and then reads the
reply using http_read_data/3. Defined options are:
\begin{description}
\termitem{connection}{ConnectionType}
If \const{close} (default) a new connection is created for this request
and closed after the request has completed. If \const{'Keep-Alive'} the
library checks for an open connection on the requested host and port
and re-uses this connection. The connection is left open if the other
party confirms the keep-alive and closed otherwise.
\termitem{http_version}{Major-Minor}
Indicate the HTTP protocol version used for the connection. Default is
\const{1.1}.
\termitem{proxy}{+Host, +Port}
Use an HTTP proxy to connect to the outside world.
\termitem{proxy_authorization}{+Authorization}
Send authorization to the proxy. Otherwise the same as the
\const{authorization} option.
\termitem{timeout}{+Timeout}
If provided, set a timeout on the stream using set_stream/2. With this
option if no new data arrives within \arg{Timeout} seconds the
stream raises an exception. Default is to wait forever
(\const{infinite}).
\termitem{user_agent}{+Agent}
Defines the value of the \const{User-Agent} field of the HTTP header.
Default is \const{SWI-Prolog (http://www.swi-prolog.org)}.
\termitem{range}{+Range}
Ask for partial content. \arg{Range} is a term \term{\arg{Unit}}{From,
To}, where \arg{From} is an integer and \arg{To} is either an integer
or the atom \const{end}. HTTP 1.1 only supports \arg{Unit} =
\const{bytes}. E.g., to ask for bytes 1000-1999, use the option
\exam{range(bytes(1000,1999))}.
\termitem{request_header}{Name = Value}
Add a line "\arg{Name}: \arg{Value}" to the HTTP request header. Both
name and value are added uninspected and literally to the request
header. This may be used to specify accept encodings, languages, etc.
Please check the RFC2616 (HTTP) document for available fields and their
meaning.
\termitem{reply_header}{Header}
Unify \arg{Header} with a list of \arg{Name}=\arg{Value} pairs
expressing all header fields of the reply. See http_read_request/2
for the result format.
\end{description}
Remaining options are passed to http_read_data/3.
\predicate{http_post}{4}{+URL, +In, -Reply, +Options}
Performs a HTTP POST request on the given URL. It is equivalent to
http_get/3, except for providing an \jargon{input document}, which is
posted using http_post_data/3.
\predicate{http_read_data}{3}{+Header, -Data, +Options}
Read data from an HTTP stream. Normally called from http_get/3 or
http_post/4. When dealing with HTTP POST in a server this predicate can
be used to retrieve the posted data. \arg{Header} is the parsed header.
\arg{Options} is a list of \term{\arg{Name}}{Value} pairs to guide the
translation of the data. The following options are supported:
\begin{description}
\termitem{to}{Target}
Do not try to interpret the data according to the MIME-type, but return
it literally according to \arg{Target}, which is one of:
\begin{description}
\termitem{stream}{Output}
Append the data to the given stream, which must be a Prolog stream open
for writing. This can be used to save the data in a (memory-)file, XPCE
object, forward it to process using a pipe, etc.
\termitem{atom}{}
Return the result as an atom. Though SWI-Prolog has no limit on the
size of atoms and provides atom-garbage collection, this options should
be used with care.%
\footnote{Currently atom-garbage collection is activated after
the creation of 10,000 atoms.}
\termitem{codes}{}
Return the page as a list of character-codes. This is especially useful
for parsing it using grammar rules.
\end{description}
\termitem{content_type}{Type}
Overrule the \const{Content-Type} as provided by the HTTP reply header.
Intended as a work-around for badly configured servers.
\end{description}
If no \term{to}{Target} option is provided the library tries the
registered plug-in conversion filters. If none of these succeed it
tries the built-in content-type handlers or returns the content as an
atom. The builtin content filters are described below. The provided
plug-ins are described in the following sections.
\begin{description}
\termitem{application/x-www-form-urlencoded}{}
This is the default encoding mechanism for POST requests issued by
a web-browser. It is broken down to a list of \arg{Name} = \arg{Value}
terms.
\end{description}
Finally, if all else fails the content is returned as an atom.
\predicate{http_post_data}{3}{+Data, +Stream, +ExtraHeader}
Write an HTTP POST request to \arg{Stream} using data from \arg{Data}
and passing the additional extra headers from \arg{ExtraHeader}.
\arg{Data} is one of:
\begin{description}
\termitem{html}{+HTMLTokens}
Send an HTML token string as produced by the library \pllib{html_write}
described in section \secref{htmlwrite}.
\termitem{file}{+File}
Send the contents of \arg{File}. The MIME type is derived from the
filename extension using file_mime_type/2.
\termitem{file}{+Type, +File}
Send the contents of \arg{File} using the provided MIME type,
i.e.\ claiming the \const{Content-type} equals \arg{Type}.
\termitem{codes}{+Codes}
Same as string(text/plain, Codes).
\termitem{codes}{+Type, +Codes}
Send string (list of character codes) using the indicated MIME-type.
\termitem{cgi_stream}{+Stream, +Len}
Read the input from \arg{Stream} which, like CGI data starts with a
partial HTTP header. The fields of this header are merged with the
provided \arg{ExtraHeader} fields. The first \arg{Len} characters
of \arg{Stream} are used.
\termitem{form}{+ListOfParameter}
Send data of the MIME type \const{application/x-www-form-urlencoded}
as produced by browsers issuing a POST request from an HTML form.
\arg{ListOfParameter} is a list of \arg{Name}=\arg{Value} or
\mbox{\arg{Name}(\arg{Value})}.
\termitem{form_data}{+ListOfData}
Send data of the MIME type \const{multipart/form-data} as produced by
browsers issuing a POST request from an HTML form using \const{enctype}
\const{multipart/form-data}. This is a somewhat simplified MIME
\const{multipart/mixed} encoding used by browser forms including
file input fields. \arg{ListOfData} is the same as for the \arg{List}
alternative described below. Below is an example from the SWI-Prolog
\url[Sesame]{http://www.openrdf.org} interface. \arg{Repository}, etc.\
are atoms providing the value, while the last argument provides a value
from a file.
\begin{code}
...,
http_post([ protocol(http),
host(Host),
port(Port),
path(ActionPath)
],
form_data([ repository = Repository,
dataFormat = DataFormat,
baseURI = BaseURI,
verifyData = Verify,
data = file(File)
]),
_Reply,
[]),
...,
\end{code}
\termitem{List}{}
If the argument is a plain list, it is sent using the MIME type
\const{multipart/mixed} and packed using mime_pack/3. See
mime_pack/3 for details on the argument format.
\end{description}
\end{description}
\subsubsection{The MIME client plug-in} \label{sec:httpmimeplugin}
This plug-in library \pllib{http/http_mime_plugin} breaks multipart
documents that are recognised by the \exam{Content-Type:
multipart/form-data} or \exam{Mime-Version: 1.0} in the header into a
list of \arg{Name} = \arg{Value} pairs. This library deals with data
from web-forms using the \const{multipart/form-data} encoding as well as
the \url[FIPA]{http://www.fipa.org} agent-protocol messages.
\subsubsection{The SGML client plug-in} \label{sec:httpsgmlplugin}
This plug-in library \pllib{http/http_sgml_plugin} provides a bridge
between the SGML/XML/HTML parser provided by \pllib{sgml} and the http
client library. After loading this hook the following mime-types are
automatically handled by the SGML parser.
\begin{description}
\termitem{text/html}{}
Handed to \pllib{sgml} using W3C HTML 4.0 DTD, suppressing and
ignoring all HTML syntax errors. \arg{Options} is passed to
load_structure/3.
\termitem{text/xml}{}
Handed to \pllib{sgml} using dialect \const{xmlns} (XML + namespaces).
\arg{Options} is passed to load_structure/3. In particular,
\term{dialect}{xml} may be used to suppress namespace handling.
\termitem{text/x-sgml}{}
Handled to \pllib{sgml} using dialect \const{sgml}. \arg{Options}
is passed to load_structure/3.
\end{description}
\section{The HTTP server libraries} \label{sec:httpserver}
The HTTP server library consists of two parts obligatory and one
optional part. The first deals with connection management and has three
different implementation depending on the desired type of server. The
second implements a generic wrapper for decoding the HTTP request,
calling user code to handle the request and encode the answer. The
optional \file{http_dispatch} module can be used to assign HTTP
\jargon{locations} (paths) to predicates. This design is summarised in
\figref{httpserver}.
\postscriptfig[width=0.8\linewidth]{httpserver}{Design of the HTTP
server}
The functional body of the user's code is independent from the selected
server-type, making it easy to switch between the supported server
types.
\subsection{The `Body'} \label{sec:body}
The server-body is the code that handles the request and formulates a
reply. To facilitate all mentioned setups, the body is driven by
http_wrapper/5. The goal is called with the parsed request (see
\secref{request}) as argument and \const{current_output} set to a
temporary buffer. Its task is closely related to the task of a CGI
script; it must write a header declaring holding at least the
\const{Content-type} field and a body. Here is a simple body writing the
request as an HTML table.
\begin{code}
reply(Request) :-
format('Content-type: text/html~n~n', []),
format('<html>~n', []),
format('<table border=1>~n'),
print_request(Request),
format('~n</table>~n'),
format('</html>~n', []).
print_request([]).
print_request([H|T]) :-
H =.. [Name, Value],
format('<tr><td>~w<td>~w~n', [Name, Value]),
print_request(T).
\end{code}
The infrastructure recognises the header
\texttt{Transfer-encoding:~chunked}, causing it to use chunked encoding
if the client allows for it. See also \secref{transfer} and the
\const{chunked} option in http_handler/3. Other header lines are passed
verbatim to the client. Typical examples are \texttt{Set-Cookie} and
authentication headers (see \secref{auth}.
\subsubsection{Returning special status codes} \label{sec:httpspecials}
Besides returning a page by writing it to the current output stream,
the server goal can raise an exception using throw/1 to generate special
pages such as \const{not_found}, \const{moved}, etc. The defined
exceptions are:
\begin{description}
\termitem{http_reply}{+Reply, +HdrExtra}
Return a result page using http_reply/3. See http_reply/3 for details.
\termitem{http_reply}{+Reply}
Equivalent to \term{http_reply}{Reply, []}.
\termitem{http}{not_modified}
Equivalent to \term{http_reply}{not_modified, []}. This exception is
for backward compatibility and can be used by the server to indicate
the referenced resource has not been modified since it was requested
last time.
\end{description}
\input{httpdispatch.tex}
\input{httpdirindex.tex}
\input{httpsession.tex}
\subsection{HTTP Authentication}
\label{sec:auth}
The module \file{http/http_authenticate} provides the basics to validate
an HTTP \const{Authorization} error. User and password information are
read from a Unix/Apache compatible password file. This information, as
well as the validation process is cached to achieve optimal performance.
\begin{description}
\predicate{http_authenticate}{+Type, +Request, -User}
True if Request contains the information to continue according
to Type. Type identifies the required authentication technique:
\begin{description}
\termitem{basic}{+PasswordFile}
Use HTTP \const{Basic} authentication and verify the password
from PasswordFile. PasswordFile is a file holding
usernames and passwords in a format compatible to
Unix and Apache. Each line is record with \verb$:$
separated fields. The first field is the username and
the second the password _hash_. Password hashes are
validated using crypt/2.
\end{description}
Successful authorization is cached for 60 seconds to avoid
overhead of decoding and lookup of the user and password data.
http_authenticate/3 just validates the header. If authorization
is not provided the browser must be challenged, in response to
which it normally opens a user-password dialogue. Example code
realising this is below. The exception causes the HTTP wrapper
code to generate an HTTP 401 reply.
\begin{code}
...,
( http_authenticate(basic(passwd), Request, User)
-> true
; throw(http_reply(authorise(basic, Realm)))
).
\end{code}
Alternatively \term{basic}{+PasswordFile} can be passed as an option to
http_handler/3.
\end{description}
\input{httpopenid.tex}
%================================================================
\subsection{Get parameters from HTML forms}
\label{sec:httpparam}
The library \pllib{http/http_parameters} provides two predicates to
fetch HTTP request parameters as a type-checked list easily. The
library transparently handles both GET and POST requests. It builds
on top of the low-level request representation described in
\secref{request}.
\begin{description}
\predicate{http_parameters}{2}{+Request, ?Parameters}
The predicate is passes the \arg{Request} as provided to the handler
goal by http_wrapper/5 as well as a partially instantiated lists
describing the requested parameters and their types. Each parameter
specification in \arg{Parameters} is a term of the format
\mbox{\arg{Name}(\arg{-Value}, \arg{+Options})}. \arg{Options} is
a list of option terms describing the type, default, etc. If no options
are specified the parameter must be present and its value is returned in
\arg{Value} as an atom. If a parameter is missing the exception
\term{error}{\term{existence_error}{form_data, Name}, _} is thrown.
Options fall into three categories: those that handle presence of
the parameter, those that guide conversion and restrict types and
those that support automatic generation of documention. First,
the presence-options:
\begin{description}
\termitem{default}{Default}
If the named parameter is missing, \arg{Value} is unified to
\arg{Default}.
\termitem{optional}{true}
If the named parameter is missing, \arg{Value} is left unbound and
no error is generated.
\termitem{list}{Type}
The same parameter may not appear or appear multiple times. If this
option is present, \const{default} and \const{optional} are ignored and
the value is returned as a list. Type checking options are processed on
each value.
\termitem{zero_or_more}{}
Deprecated. Use \term{List}{Type}.
\end{description}
The type and conversion options are given below. The type-language can
be extended by providing clauses for the multifile hook
http:convert_parameter/3.
\begin{description}
\termitem{;}{Type1, Type2}
Succeed if either \arg{Type1} or \arg{Type2} applies. It allows
for checks such as \exam{(nonneg;oneof([infinite]))} to specify
an integer or a symbolic value.
\termitem{oneof}{List}
Succeeds if the value is member of the given list.
\definition{length $> N$}
Succeeds if value is an atom of more than $N$ characters.
\definition{length $>= N$}
Succeeds if value is an atom of more or than equal to $N$ characters.
\definition{length $< N$}
Succeeds if value is an atom of less than $N$ characters.
\definition{length $=< N$}
Succeeds if value is an atom of length than or equal to $N$ characters.
\termitem{atom}{}
No-op. Allowed for consistency.
\termitem{between}{+Low, +High}
Convert value to a number and if either \arg{Low} or \arg{High} is a
float, force value to be a float. Then check that the value is in the
given range, which includes the boundaries.
\termitem{boolean}{}
Translate =true=, =yes=, =on= and '1' into =true=; =false=, =no=,
=off= and '0' into =false= and raises an error otherwise.
\termitem{float}{}
Convert value to a float. Integers are transformed into float. Throws a
type-error otherwise.
\termitem{integer}{}
Convert value to an integer. Throws a type-error otherwise.
\termitem{nonneg}{}
Convert value to a non-negative integer. Throws a type-error
of the value cannot be converted to an integer and a domain-error
otherwise.
\termitem{number}{}
Convert value to a number. Throws a type-error otherwise.
\end{description}
The last set of options is to support automatic generation of HTTP
API documentation from the sources.\footnote{This facility is under
development in ClioPatria; see \file{http_help.pl}}.
\begin{description}
\termitem{description}{+Atom}
Description of the parameter in plain text.
\termitem{group}{+Parameters, +Options}
Define a logical group of parameters. \arg{Parameters} are processed
as normal. \arg{Options} may include a description of the group. Groups
can be nested.
\end{description}
Below is an example
\begin{code}
reply(Request) :-
http_parameters(Request,
[ title(Title, [ optional(true) ]),
name(Name, [ length >= 2 ]),
age(Age, [ between(0, 150) ])
]),
...
\end{code}
Same as \term{http_parameters}{Request, Parameters, []}
\predicate{http_parameters}{3}{+Request, ?Parameters, +Options}
In addition to http_parameters/2, the following options are defined.
\begin{description}
\termitem{form_data}{-Data}
Return the entire set of provided \arg{Name}=\arg{Value} pairs from
the GET or POST request. All values are returned as atoms.
\termitem{attribute_declarations}{:Goal}
If a parameter specification lacks the parameter options, call
\term{call}{Goal, +ParamName, -Options} to find the options. Intended
to share declarations over many calls to http_parameters/3. Using
this construct the above can be written as below.
\begin{code}
reply(Request) :-
http_parameters(Request,
[ title(Title),
name(Name),
age(Age)
],
[ attribute_declarations(param)
]),
...
param(title, [optional(true)]).
param(name, [length >= 2 ]).
param(age, [integer]).
\end{code}
\end{description}
\end{description}
\subsection{Request format} \label{sec:request}
The body-code (see \secref{body}) is driven by a \arg{Request}. This
request is generated from http_read_request/2 defined in
\pllib{http/http_header}.
\begin{description}
\predicate{http_read_request}{2}{+Stream, -Request}
Reads an HTTP request from \arg{Stream} and unify \arg{Request} with
the parsed request. \arg{Request} is a list of \term{\arg{Name}}{Value}
elements. It provides a number of predefined elements for the result
of parsing the first line of the request, followed by the additional
request parameters. The predefined fields are:
\begin{description}
\termitem{host}{Host}
If the request contains \verb$Host: $\arg{Host}, Host is unified
with the host-name. If \arg{Host} is of the format <host>:<port>
\arg{Host} only describes <host> and a field \term{port}{Port} where
\arg{Port} is an integer is added.
\termitem{input}{Stream}
The \arg{Stream} is passed along, allowing to read more data or
requests from the same stream. This field is always present.
\termitem{method}{Method}
\arg{Method} is one of \const{get}, \const{put} or \const{post}. This
field is present if the header has been parsed successfully.
\termitem{path}{Path}
Path associated to the request. This field is always present.
\termitem{peer}{Peer}
\arg{Peer} is a term \term{ip}{A,B,C,D} containing the IP address of
the contacting host.
\termitem{port}{Port}
Port requested. See \const{host} for details.
\termitem{request_uri}{RequestURI}
This is the untranslated string that follows the method in the
request header. It is used to construct the path and search fields
of the \arg{Request}. It is provided because reconstructing this
string from the path and search fields may yield a different value
due to different usage of percent encoding.
\termitem{search}{ListOfNameValue}
Search-specification of URI. This is the part after the \chr{?},
normally used to transfer data from HTML forms that use the
`\const{GET}' protocol. In the URL it consists of a www-form-encoded
list of \arg{Name}=\arg{Value} pairs. This is mapped to a list of
Prolog \arg{Name}=\arg{Value} terms with decoded names and values.
This field is only present if the location contains a
search-specification.
\termitem{http_version}{Major-Minor}
If the first line contains the \const{HTTP/}\arg{Major}.\arg{Minor}
version indicator this element indicate the HTTP version of the
peer. Otherwise this field is not present.
\termitem{cookie}{ListOfNameValue}
If the header contains a \const{Cookie} line, the value of the
cookie is broken down in \arg{Name}=\arg{Value} pairs, where the
\arg{Name} is the lowercase version of the cookie name as used
for the HTTP fields.
\termitem{set_cookie}{set_cookie(Name, Value, Options)}
If the header contains a \const{SetCookie} line, the cookie field
is broken down into the \arg{Name} of the cookie, the \arg{Value}
and a list of \arg{Name}=\arg{Value} pairs for additional options
such as \const{expire}, \const{path}, \const{domain} or \const{secure}.
\end{description}
If the first line of the request is tagged with
\const{HTTP/}\arg{Major}.\arg{Minor}, http_read_request/2 reads all
input upto the first blank line. This header consists of
\arg{Name}:\arg{Value} fields. Each such field appears as a term
\term{\arg{Name}}{Value} in the \arg{Request}, where \arg{Name} is
canonised for use with Prolog. Canonisation implies that the
\arg{Name} is converted to lower case and all occurrences of the
\chr{-} are replaced by \chr{_}. The value for the
\const{Content-length} fields is translated into an integer.
\end{description}
Here is an example:
\begin{code}
?- http_read_request(user, X).
|: GET /mydb?class=person HTTP/1.0
|: Host: gollem
|:
X = [ input(user),
method(get),
search([ class = person
]),
path('/mydb'),
http_version(1-0),
host(gollem)
].
\end{code}
\subsubsection{Handling POST requests}
Where the HTTP \const{GET} operation is intended to get a document,
using a \arg{path} and possibly some additional search information,
the \const{POST} operation is intended to hand potentially large
amounts of data to the server for processing.
The \arg{Request} parameter above contains the term \term{method}{post}.
The data posted is left on the input stream that is available through
the term \term{input}{Stream} from the \arg{Request} header. This data
can be read using http_read_data/3 from the HTTP client library. Here is
a demo implementation simply returning the parsed posted data as plain
text (assuming pp/1 pretty-prints the data).
\begin{code}
reply(Request) :-
member(method(post), Request), !,
http_read_data(Request, Data, []),
format('Content-type: text/plain~n~n', []),
pp(Data).
\end{code}
If the POST is initiated from a browser, content-type is generally
either \const{application/x-www-form-urlencoded} or
\const{multipart/form-data}. The latter is broken down automatically
if the plug-in \pllib{http/http_mime_plugin} is loaded.
\subsection{Running the server}
The functionality of the server should be defined in one Prolog file (of
course this file is allowed to load other files). Depending on the
wanted server setup this `body' is wrapped into a small Prolog file
combining the body with the appropriate server interface. There are
three supported server-setups. For most applications we advice the
multi-threaded server. Examples of this server architecture are the
\url[PlDoc]{http://www.swi-prolog.org/packages/pldoc.html} documentation
system and the \url[SeRQL]{http://www.swi-prolog.org/packages/SeRQL/}
Semantic Web server infrastructure.
All the server setups may be wrapped in a \jargon{reverse proxy} to
make them available from the public web-server as described in
\secref{proxy}.
\begin{itemlist}
\item [Using \pllib{thread_httpd} for a multi-threaded server]
This server exploits the multi-threaded version of SWI-Prolog, running
the users body code parallel from a pool of worker threads. As it avoids
the state engine and copying required in the event-driven server it is
generally faster and capable to handle multiple requests concurrently.
This server is harder to debug due to the involved threading, although
the GUI tracer provides reasonable support for multi-threaded
applications using the tspy/1 command. It can provide fast communication
to multiple clients and can be used for more demanding servers.
\item [Using \pllib{xpce_httpd} for an event-driven server]
This approach provides a single-threaded event-driven application. The
clients talk to XPCE sockets that collect an HTTP request. The server
infra-structure can talk to multiple clients simultaneously, but once
a request is complete the wrappers call the user's goal and blocks all
further activity until the request is handled. Requests from multiple
clients are thus fully serialised in one Prolog process.
This server setup is very suitable for debugging as well as embedded
server in simple applications in a fairly controlled environment.
\item [Using \pllib{inetd_httpd} for server-per-client]
In this setup the Unix \program{inetd} user-daemon is used to initialise
a server for each connection. This approach is especially suitable for
servers that have a limited startup-time. In this setup a crashing
client does not influence other requests.
This server is very hard to debug as the server is not connected to the
user environment. It provides a robust implementation for servers that
can be started quickly.
\end{itemlist}
\subsubsection{Common server interface options}
All the server interfaces provide \term{http_server}{:Goal, +Options}
to create the server. The list of options differ, but the servers share
common options:
\begin{description}
\termitem{port}{?Port}
Specify the port to listen to for stand-alone servers. \arg{Port} is
either an integer or unbound. If unbound, it is unified to the selected
free port.
\end{description}
\subsubsection{Multi-threaded Prolog} \label{sec:mthttpd}
The \pllib{http/thread_httpd.pl} provides the infrastructure to manage
multiple clients using a pool of \jargon{worker-threads}. This realises
a popular server design, also seen in Java Tomcat and Microsoft .NET.
As a single persistent server process maintains communication to all
clients startup time is not an important issue and the server can
easily maintain state-information for all clients.
In addition to the functionality provided by the other (XPCE and
inetd) servers, the threaded server can also be used to realise an
HTTPS server exploiting the \pllib{ssl} library. See option
\term{ssl}{+SSLOptions} below.
\begin{description}
\predicate{http_server}{3}{:Goal, +Options}
Create the server. \arg{Options} must provide the \term{port}{?Port}
option to specify the port the server should listen to. If \arg{Port} is
unbound an arbitrary free port is selected and \arg{Port} is unified to
this port-number. The server consists of a small Prolog thread
accepting new connection on \arg{Port} and dispatching these to a pool
of workers. Defined \arg{Options} are:
\begin{description}
\termitem{port}{?Port}
Port the server should listen to. If unbound \arg{Port} is unified with
the selected free port.
\termitem{workers}{+N}
Defines the number of worker threads in the pool. Default is to use
\arg{two} workers. Choosing the optimal value for best performance is a
difficult task depending on the number of CPUs in your system and how
much resources are required for processing a request. Too high numbers
makes your system switch too often between threads or even swap if there
is not enough memory to keep all threads in memory, while a too low
number causes clients to wait unnecessary for other clients to complete.
See also http_workers/2.
\termitem{timeout}{+SecondsOrInfinite}
Determines the maximum period of inactivity handling a request. If no
data arrives within the specified time since the last data arrived the
connection raises an exception, the worker discards the client and
returns to the pool-queue for a new client. Default is \const{infinite},
making each worker wait forever for a request to complete. Without a
timeout, a worker may wait forever on an a client that doesn't complete
its request.
\termitem{keep_alive_timeout}{+SecondsOrInfinite}
Maximum time to wait for new activity on \emph{Keep-Alive} connections.
Choosing the correct value for this parameter is hard. Disabling
Keep-Alive is bad for performance if the clients request multiple
documents for a single page. This may ---for example-- be caused by HTML
frames, HTML pages with images, associated CSS files, etc. Keeping
a connection open in the threaded model however prevents the thread
servicing the client servicing other clients. The default is 5 seconds.
\termitem{local}{+KBytes}
Size of the local-stack for the workers. Default is taken from the
commandline option.
\termitem{global}{+KBytes}
Size of the global-stack for the workers. Default is taken from the
commandline option.
\termitem{trail}{+KBytes}
Size of the trail-stack for the workers. Default is taken from the
commandline option.
\termitem{ssl}{+SSLOptions}
Use SSL (Secure Socket Layer) rather than plan TCP/IP. A server created
this way is accessed using the \const{https://} protocol. SSL allows for
encrypted communication to avoid others from tapping the wire as well as
improved authentication of client and server. The \arg{SSLOptions}
option list is passed to ssl_init/3. The port option of the main option
list is forwarded to the SSL layer. See the \pllib{ssl} library for
details.
\end{description}
\predicate{http_server_property}{2}{?Port, ?Property}
True if \arg{Property} is a property of the HTTP server running at
\arg{Port}. Defined properties are:
\begin{description}
\termitem{goal}{:Goal}
Goal used to start the server. This is often http_dispatch/1.
\termitem{start_time}{?Time}
Time-stamp when the server was created. See format_time/3 for
creating a human-readable representation.
\end{description}
\predicate{http_workers}{2}{:Port, ?Workers}
Query or manipulate the number of workers of the server identified by
\arg{Port}. If \arg{Workers} is unbound it is unified with the number
of running servers. If it is an integer greater than the current size
of the worker pool new workers are created with the same specification
as the running workers. If the number is less than the current size
of the worker pool, this predicate inserts a number of `quit' requests
in the queue, discarding the excess workers as they finish their jobs
(i.e.\ no worker is abandoned while serving a client).
This can be used to tune the number of workers for performance. Another
possible application is to reduce the pool to one worker to facilitate
easier debugging.
\predicate{http_stop_server}{2}{+Port, +Options}
Stop the HTTP server at Port. Halting a server is done
\textit{gracefully}, which means that requests being processed are not
abandoned. The \arg{Options} list is for future refinements of this
predicate such as a forced immediate abort of the server, but is
currently ignored.
\predicate{http_current_worker}{2}{?Port, ?ThreadID}
True if \arg{ThreadID} is the identifier of a Prolog thread serving
\arg{Port}. This predicate is motivated to allow for the use of
arbitrary interaction with the worker thread for development and
statistics.
\predicate{http_spawn}{2}{:Goal, +Spec}
Continue handling this request in a new thread running \arg{Goal}. After
http_spawn/2, the worker returns to the pool to process new requests. In
its simplest form, \arg{Spec} is the name of a thread pool as defined by
thread_pool_create/3. Alternatively it is an option list, whose options
are passed to thread_create_in_pool/4 if \arg{Spec} contains
\term{pool}{Pool} or to thread_create/3 of the pool option is not
present. If the dispatch module is used (see \secref{httpdispatch}),
spawning is normally specified as an option to the http_handler/3
registration.
We recomment the use of thread pools. They allow registration of a set
of threads using common characteristics, specify how many can be active
and what to do if all threads are active. A typical application may
define a small pool of threads with large stacks for computation
intensive tasks, and a large pool of threads with small stacks to serve
media. The declaration could be the one below, allowing for max 3
concurrent solvers and a maximum backlog of 5 and 30 tasks creating
image thumbnails.
\begin{code}
:- use_module(library(thread_pool)).
:- thread_pool_create(compute, 3,
[ local(20000), global(100000), trail(50000),
backlog(5)
]).
:- thread_pool_create(media, 30,
[ local(100), global(100), trail(100),
backlog(100)
]).
:- http_handler('/solve', solve, [spawn(compute)]).
:- http_handler('/thumbnail', thumbnail, [spawn(media)]).
\end{code}
\end{description}
\subsubsection{From an interactive Prolog session using XPCE}
The \pllib{http/xpce_httpd.pl} provides the infrastructure to manage
multiple clients with an event-driven control-structure. This version
can be started from an interactive Prolog session, providing a
comfortable infra-structure to debug the body of your server. It also
allows the combination of an (XPCE-based) GUI with web-technology in one
application.
\begin{description}
\predicate{http_server}{2}{:Goal, +Options}
Create an instance of \class{interactive_httpd}. \arg{Options} must
provide the \term{port}{?Port} option to specify the port the server
should listen to. If \arg{Port} is unbound an arbitrary free port is
selected and \arg{Port} is unified to this port-number. Currently
no options are defined.
\end{description}
The file \file{demo_xpce} gives a typical example of this wrapper,
assuming \file{demo_body} defines the predicate reply/1.
\begin{code}
:- use_module(xpce_httpd).
:- use_module(demo_body).
server(Port) :-
http_server(reply, Port, []).
\end{code}
The created server opens a server socket at the selected address and
waits for incoming connections. On each accepted connection it collects
input until an HTTP request is complete. Then it opens an input stream
on the collected data and using the output stream directed to the XPCE
\class{socket} it calls http_wrapper/5. This approach is fundamentally
different compared to the other approaches:
\begin{itemlist}
\item [Server can handle multiple connections]
When \emph{inetd} will start a server for each \emph{client}, and CGI
starts a server for each \emph{request}, this approach starts a single
server handling multiple clients.
\item [Requests are serialised]
All calls to \arg{Goal} are fully serialised, processing on behalf of a
new client can only start after all previous requests are answered. This
easier and quite acceptable if the server is mostly inactive and
requests take not very long to process.
\item [Lifetime of the server]
The server lives as long as Prolog runs.
\end{itemlist}
\subsubsection{From (Unix) inetd}
All modern Unix systems handle a large number of the services they run
through the super-server \emph{inetd}. This program reads
\file{/etc/inetd.conf} and opens server-sockets on all ports defined in
this file. As a request comes in it accepts it and starts the associated
server such that standard I/O refers to the socket. This approach has
several advantages:
\begin{itemlist}
\item [Simplification of servers]
Servers don't have to know about sockets and -operations.
\item [Centralised authorisation]
Using \emph{tcpwrappers} simple and effective firewalling of all
services is realised.
\item [Automatic start and monitor]
The inetd automatically starts the server `just-in-time' and starts
additional servers or restarts a crashed server according to the
specifications.
\end{itemlist}
The very small generic script for handling inetd based connections
is in \file{inetd_httpd}, defining http_server/1:
\begin{description}
\predicate{http_server}{2}{:Goal, +Options}
Initialises and runs http_wrapper/5 in a loop until failure or
end-of-file. This server does not support the \arg{Port} option
as the port is specified with the \program{inetd} configuration.
The only supported option is \arg{After}.
\end{description}
Here is the example from \file{demo_inetd}
\begin{code}
#!/usr/bin/pl -t main -q -f
:- use_module(demo_body).
:- use_module(inetd_httpd).
main :-
http_server(reply).
\end{code}
With the above file installed in \file{/home/jan/plhttp/demo_inetd},
the following line in \file{/etc/inetd} enables the server at port
4001 guarded by \emph{tcpwrappers}. After modifying inetd, send the
daemon the \const{HUP} signal to make it reload its configuration.
For more information, please check \manref{inetd.conf}{5}.
\begin{code}
4001 stream tcp nowait nobody /usr/sbin/tcpd /home/jan/plhttp/demo_inetd
\end{code}
\subsubsection{MS-Windows}
There are rumours that \emph{inetd} has been ported to Windows.
\subsubsection{As CGI script}
To be done.
\subsubsection{Using a reverse proxy}
\label{sec:proxy}
There are three options for public deployment of a service. One is to
run it on a dedicated machine on port 80, the standard HTTP port. The
machine may be a virtual machine running ---for example--- under
\url[VMWARE]{http://www.vmware.com} or
\url[XEN]{http://www.cl.cam.ac.uk/research/srg/netos/xen/}. The
(virtual) machine approach isolates security threads and allows for
using a standard port. The server can also be hosted on a non-standard
port such as 8000, or 8080. Using non-standard ports however may cause
problems with intermediate proxy- and/or firewall policies. Isolation
can be achieved using a Unix \jargon{chroot} environment. Another
option, also recommended for \jargon{Tomcat} servers, is the use of
Apache \jargon{reverse proxies}. This causes the main web-server to
relay requests below a given URL location to our Prolog based server.
This approach has several advantages:
\begin{itemize}
\item We can access the server on port 80, just as for a dedicated
machine. We do not need a machine though and we only need
access to the Apache configuration.
\item As Apache is doing the front-line service, the Prolog server
is normally protected from malformed HTTP requests that could
result in denial of service or otherwise compromise the
server. In addition, Apache can provide encodings such as
compression to the outside world.
\end{itemize}
Note that the proxy technology can be combined with isolation methods
such as dedicated machines, virtual machines and chroot jails. The
proxy can also provide load balancing.
\paragraph{Setting up a reverse proxy}
The Apache reverse proxy setup is really simple. Ensure the modules
\const{proxy} and \const{proxy_http} are loaded. Then add two simple
rules to the server configuration. Below is an example that makes a
PlDoc server on port 4000 available from the main Apache server at port
80.
\begin{code}
ProxyPass /pldoc/ http://localhost:4000/pldoc/
ProxyPassReverse /pldoc/ http://localhost:4000/pldoc/
\end{code}
Apache rewrites the HTTP headers passing by, but using the above rules
it does not examine the content. This implies that URLs embedded in the
(HTML) content must use relative addressing. If the locations on the
public and Prolog server are the same (as in the example above) it is
allowed to use absolute locations. I.e.\ \const{/pldoc/search} is ok,
but \const{http://myhost.com:4000/pldoc/search} is \emph{not}. If
the locations on the server differ, locations must be relative (i.e.\
not start with \chr{/}.
This problem can also be solved using the contributed Apache module
\const{proxy_html} that can be instructed to rewrite URLs embedded in
HTML documents. In our experience, this is not troublefree as URLs can
appear in many places in generated documents. JavaScript can create
URLs on the fly, which makes rewriting virtually impossible.
\subsection{The wrapper library}
The body is called by the module \pllib{http/http_wrapper.pl}. This
module realises the communication between the I/O streams and the body
described in \secref{body}. The interface is realised by
http_wrapper/5:
\begin{description}
\predicate{http_wrapper}{5}{:Goal, +In, +Out, -Connection, +Options}
Handle an HTTP request where \arg{In} is an input stream from the
client, \arg{Out} is an output stream to the client and \arg{Goal}
defines the goal realising the body. \arg{Connection} is unified to
\const{'Keep-alive'} if both ends of the connection want to continue the
connection or \const{close} if either side wishes to close the
connection.
This predicate reads an HTTP request-header from \arg{In}, redirects
current output to a memory file and then runs \exam{call(Goal,
Request)}, watching for exceptions and failure. If \arg{Goal} executes
successfully it generates a complete reply from the created output.
Otherwise it generates an HTTP server error with additional context
information derived from the exception.
http_wrapper/5 supports the following options:
\begin{description}
\termitem{request}{-Request}
Return the executed request to the caller.
\termitem{peer}{+Peer}
Add peer(Peer) to the request header handed to \arg{Goal}. The format
of \arg{Peer} is defined by tcp_accept/3 from the clib package.
\end{description}
\predicate{http:request_expansion}{2}{+RequestIn, -RequestOut}
This \jargon{multifile} hook predicate is called just before the goal
that produces the body, while the output is already redirected to
collect the reply. If it succeeds it must return a valid modified
request. It is allowed to throw exceptions as defined in
\secref{httpspecials}. It is intended for operations such as mapping
paths, deny access for certain requests or manage cookies. If it writes
output, these must be HTTP header fields that are added \emph{before}
header fields written by the body. The example below is from the
session management library (see \secref{httpsession}) sets a cookie.
\begin{code}
...,
format('Set-Cookie: ~w=~w; path=~w~n', [Cookie, SessionID, Path]),
...,
\end{code}
\predicate{http_current_request}{1}{-Request}
Get access to the currently executing request. \arg{Request} is the
same as handed to \arg{Goal} of http_wrapper/5 \emph{after} applying
rewrite rules as defined by http:request_expansion/2. Raises an
existence error if there is no request in progress.
\predicate{http_relative_path}{2}{+AbsPath, -RelPath}
Convert an absolute path (without host, fragment or search) into a path
relative to the current page, defined as the path component from the
current request (see http_current_request/1). This call is intended to
create reusable components returning relative paths for easier support
of reverse proxies.
If ---for whatever reason--- the conversion is not possible it simply
unifies \arg{RelPath} to \arg{AbsPath}.
\end{description}
\input{httphost}
\input{httplog}
\subsection{Debugging Servers} \label{sec:debug}
The library \pllib{http/http_error.pl} defines a hook that decorates
uncaught exceptions with a stack-trace. This will generate a \emph{500
internal server error} document with a stack-trace. To enable this
feature, simply load this library. Please do note that providing
error information to the user simplifies the job of a hacker trying
to compromise your server. It is therefore not recommended to load
this file by default.
The example program \file{calc.pl} has the error handler loaded which
can be triggered by forcing a divide-by-zero in the calculator.
\subsection{Handling HTTP headers} \label{sec:httpheader}
The library \pllib{http/http_header} provides primitives for parsing and
composing HTTP headers. Its functionality is normally hidden by the
other parts of the HTTP server and client libraries. We provide a brief
overview of http_reply/3 which can be accessed from the reply body using
an exception as explain in \secref{httpspecials}.
\begin{description}
\predicate{http_reply}{3}{+Type, +Stream, +HdrExtra}
Compose a complete HTTP reply from the term \arg{Type} using additional
headers from \arg{HdrExtra} to the output stream \arg{Stream}.
\arg{ExtraHeader} is a list of \term{Field}{Value}. \arg{Type} is
one of:
\begin{description}
\termitem{html}{+HTML}
Produce a HTML page using print_html/1, normally generated using the
\pllib{http/html_write} described in \secref{htmlwrite}.
\termitem{file}{+MimeType, +Path}
Reply the content of the given file, indicating the given MIME type.
\termitem{tmp_file}{+MimeType, +Path}
Similar to \term{File}{+MimeType, +Path}, but do not include a
modification time header.
\termitem{stream}{+Stream, +Len}
Reply using the next \arg{Len} characters from \arg{Stream}. The
user must provides the MIME type and other attributes through the
\arg{ExtraHeader} argument.
\termitem{cgi_stream}{+Stream, +Len}
Similar to \term{stream}{+Stream, +Len}, but the data on \arg{Stream}
must contain an HTTP header.
\termitem{moved}{+URL}
Generate a ``301 Moved Permanently'' page with the given target
\arg{URL}.
\termitem{moved_temporary}{+URL}
Generate a ``302 Moved Temporary'' page with the given target
\arg{URL}.
\termitem{see_other}{+URL}
Generate a ``303 See Other'' page with the given target \arg{URL}.
\termitem{not_found}{+URL}
Generate a ``404 Not Found'' page.
\termitem{forbidden}{+URL}
Generate a ``403 Forbidden'' page, denying access without challenging
the client.
\termitem{authorise}{+Method, +Realm}
Generate a ``401 Authorization Required'', requesting the client to
retry using proper credentials (i.e.\ user and password).
\termitem{not_modified}{}
Generate a ``304 Not Modified'' page, indicating the requested resource
has not changed since the indicated time.
\termitem{server_error}{+Error}
Generate a ``500 Internal server error'' page with a message generated
from a Prolog exception term (see print_message/2).
\end{description}
\end{description}
\subsection{The \pllib{http/html_write} library} \label{sec:htmlwrite}
\newcommand{\elem}[1]{\const{#1}}
Producing output for the web in the form of an HTML document is a
requirement for many Prolog programs. Just using format/2 is
satisfactory as it leads to poorly readable programs generating poor
HTML. This library is based on using DCG rules.
The \pllib{http/html_write} structures the generation of HTML from a
program. It is an extensible library, providing a \jargon{DCG} framework
for generating legal HTML under (Prolog) program control. It is
especially useful for the generation of structured pages (e.g.\ tables)
from Prolog data structures.
The normal way to use this library is through the DCG html//1. This
non-terminal provides the central translation from a structured term
with embedded calls to additional translation rules to a list of atoms
that can then be printed using print_html/[1,2].
\begin{description}
\dcg{html}{1}{:Spec}
The DCG non-terminal html//1 is the main predicate of this library. It translates
the specification for an HTML page into a list of atoms that can be
written to a stream using print_html/[1,2]. The expansion rules of this
predicate may be extended by defining the multifile DCG
html_write:expand//1. \arg{Spec} is either a single specification or a
list of single specifications. Using nested lists is not allowed to
avoid ambiguity caused by the atom \const{[]}
\begin{itemlist}
\item [Atomic data]
Atomic data is quoted using html_quoted//1.
\item [\arg{Fmt} - \arg{Args}]
\arg{Fmt} and \arg{Args} are used as format-specification and argument
list to format/3. The result is quoted and added to the output list.
\item [\bsl\arg{List}]
Escape sequence to add atoms directly to the output list. This can be
used to embed external HTML code or emit script output. \arg{List} is
a list of the following terms:
\begin{itemlist}
\item [\arg{Fmt} - \arg{Args}]
\arg{Fmt} and \arg{Args} are used as format-specification and argument
list to format/3. The result is added to the output list.
\item [\arg{Atomic}]
Atomic values are added directly to the output list.
\end{itemlist}
\item [\bsl\arg{Term}]
Invoke the non-terminal \arg{Term} in the calling module. This is the
common mechanism to realise abstraction and modularisation in generating
HTML.
\item [\arg{Module}:\arg{Term}]
Invoke the non-terminal <Module>:<Term>. This is similar to
\bsl\arg{Term} but allows for invoking grammar rules in external
packages.
\item [\&(Entity)]
Emit {\tt\&<Entity>;} or {\tt\&\#<Entity>;} if \arg{Entity} is an
integer. SWI-Prolog atoms and strings are represented as Unicode.
Explicit use of this construct is rarely needed because code-points that
are not supported by the output encoding are automatically converted
into character-entities.
\item [\term{Tag}{Content}]
Emit HTML element \arg{Tag} using \arg{Content} and no attributes.
\arg{Content} is handed to html//1. See \secref{htmllayout} for details
on the automatically generated layout.
\item [\term{Tag}{Attributes, Content}]
Emit HTML element \arg{Tag} using \arg{Attributes} and \arg{Content}.
\arg{Attributes} is either a single attribute of a list of attributes.
Each attributes is of the format \term{Name}{Value} or
\mbox{\arg{Name}=\arg{Value}}. \arg{Value} is the atomic attribute
value but allows for a limited functional notation:
\begin{itemlist}
\item [$A$ + $B$]
Concatenation of $A$ and $B$
\item [\term{encode}{Atom}]
Use www_form_encode/2 to create a valid URL component.
\item [\term{location_by_id}{ID}]
HTTP location of the HTTP handler with given ID. See http_location_by_id/2.
\item [List]
A list is handled as a URL `search' component. The list members are
terms of the format \mbox{\arg{Name} = \arg{Value}} or
\term{Name}{Value}. Values are encoded as in the encode option
described above.
\end{itemlist}
The example below generates a URL that references the predicate
set_lang/1 in the application with given parameters. The http_handler/3
declaration binds \const{/setlang} to the predicate set_lang/1 for which
we provide a very simple implementation. The code between \const{...}
is part of an HTML page showing the english flag which, when pressed,
calls \term{set_lang}{Request} where \arg{Request} contains the search
parameter \mbox{\const{lang} = \const{en}}. Note that the HTTP location
(path) \const{/setlang} can be moved without affecting this code.
\begin{code}
:- http_handler('/setlang', set_lang, []).
set_lang(Request) :-
http_parameters(Request,
[ lang(Lang, [])
]),
http_session_retractall(lang(_)),
http_session_assert(lang(Lang)),
reply_html_page(title('Switched language'),
p(['Switch language to ', Lang])).
...
html(a(href(location_by_id(set_lang) + [lang(en)]),
img(src('/www/images/flags/en.png')))),
...
\end{code}
\end{itemlist}
\dcg{page}{2}{:HeadContent, :BodyContent}
The DCG non-terminal page//2 generated a complete page, including the SGML
\const{DOCTYPE} declaration. \arg{HeadContent} are elements to be placed
in the \elem{head} element and \arg{BodyContent} are elements to be
placed in the \elem{body} element.
To achieve common style (background, page header and footer), it is
possible to define DCG non-terminals head//1 and/or body//1. Non-terminal page//1
checks for the definition of these non-terminals in the module it is called
from as well as in the \const{user} module. If no definition is found, it
creates a head with only the \arg{HeadContent} (note that the
\elem{title} is obligatory) and a \elem{body} with \const{bgcolor} set
to \const{white} and the provided \arg{BodyContent}.
Note that further customisation is easily achieved using html//1 directly
as page//2 is (besides handling the hooks) defined as:
\begin{code}
page(Head, Body) -->
html([ \['<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 4.0//EN">\n'],
html([ head(Head),
body(bgcolor(white), Body)
])
]).
\end{code}
\dcg{page}{1}{:Contents}
This version of the page/[1,2] only gives you the SGML \const{DOCTYPE}
and the \elem{HTML} element. \arg{Contents} is used to generate both the
head and body of the page.
\dcg{html_begin}{1}{+Begin}
Just open the given element. \arg{Begin} is either an atom or a
compound term, In the latter case the arguments are used as arguments
to the begin-tag. Some examples:
\begin{code}
html_begin(table)
html_begin(table(border(2), align(center)))
\end{code}
This predicate provides an alternative to using the
\bsl\arg{Command} syntax in the html//1 specification. The
following two fragments are the same. The preferred solution depends on
your preferences as well as whether the specification is generated or
entered by the programmer.
\begin{code}
table(Rows) -->
html(table([border(1), align(center), width('80%')],
[ \table_header,
\table_rows(Rows)
])).
% or
table(Rows) -->
html_begin(table(border(1), align(center), width('80%'))),
table_header,
table_rows,
html_end(table).
\end{code}
\dcg{html_end}{1}{+End}
End an element. See html_begin/1 for details.
\end{description}
\subsubsection{Emitting HTML documents}
The non-terminal html//1 translates a specification into a list of
atoms and layout instructions. Currently the layout instructions are
terms of the format \term{nl}{N}, requesting at least \arg{N}
newlines. Multiple consecutive \term{nl}{1} terms are combined to an
atom containing the maximum of the requested number of newline
characters.
To simplify handing the data to a client or storing it into a file,
the following predicates are available from this library:
\begin{description}
\predicate{reply_html_page}{2}{:Head, :Body}
Same as \term{reply_html_page}{default, Head, Body}.
\predicate{reply_html_page}{3}{+Style, :Head, :Body}
Writes an HTML page preceded by an HTTP header as required
by \pllib{http_wrapper} (CGI-style). Here is a simple typical
example:
\begin{code}
reply(Request) :-
reply_html_page(title('Welcome'),
[ h1('Welcome'),
p('Welcome to our ...')
]).
\end{code}
The header and footer of the page can be hooked using the grammar-rules
user:head//2 and user:body//2. The first argument passed to these hooks
is the \arg{Style} argument of reply_html_page/3 and the second is the
2nd (for head//2) or 3rd (for body//2) argument of reply_html_page/3.
These hooks can be used to restyle the page, typically by embedding the
real body content in a \elem{div}. E.g., the following code provides a
menu on top of each page of that is identified using the style
\textit{myapp}.
\begin{code}
:- multifile
user:body//2.
user:body(myapp, Body) -->
html(body([ div(id(top), \application_menu),
div(id(content), Body)
])).
\end{code}
Redefining the \elem{head} can be used to pull in scripts, but
typically html_requires//1 provides a more modular approach for
pulling scripts and CSS-files.
\predicate{print_html}{1}{+List}
Print the token list to the Prolog current output stream.
\predicate{print_html}{2}{+Stream, +List}
Print the token list to the specified output stream
\predicate{html_print_length}{2}{+List, -Length}
When calling html_print/[1,2] on \arg{List}, \arg{Length}
characters will be produced. Knowing the length is needed to
provide the \const{Content-length} field of an HTTP reply-header.
\end{description}
\input{post.tex}
\subsubsection{Adding rules for html//1}
In some cases it is practical to extend the translations imposed by
html//1. When using XPCE for example, it is comfortable to be able
defining default translation to HTML for objects. We also used this
technique to define translation rules for the output of the SWI-Prolog
\pllib{sgml} package.
The html//1 non-terminal first calls the multifile ruleset html_write:expand//1.
\begin{description}
\dcg{html_write:expand}{1}{+Spec} Hook to add additional
translation rules for html//1.
\dcg{html_quoted}{1}{+Atom} Emit the text
in \arg{Atom}, inserting entity-references for the SGML special
characters \verb$<&>$.
\dcg{html_quoted_attribute}{1}{+Atom} Emit the
text in \arg{Atom} suitable for use as an SGML attribute, inserting
entity-references for the SGML special characters \verb$<&>"$.
\end{description}
\subsubsection{Generating layout} \label{sec:htmllayout}
Though not strictly necessary, the library attempts to generate
reasonable layout in SGML output. It does this only by inserting
newlines before and after tags. It does this on the basis of the
multifile predicate html_write:layout/3
\begin{description}
\predicate{html_write:layout}{3}{+Tag, -Open, -Close}
Specify the layout conventions for the element \arg{Tag}, which is a
lowercase atom. \arg{Open} is a term \arg{Pre}-\arg{Post}. It defines
that the element should have at least \arg{Pre} newline characters
before and \arg{Post} after the tag. The \arg{Close} specification is
similar, but in addition allows for the atom \const{-}, requesting the
output generator to omit the close-tag altogether or \const{empty},
telling the library that the element has declared empty content. In this
case the close-tag is not emitted either, but in addition html//1
interprets \arg{Arg} in \term{Tag}{Arg} as a list of attributes rather
than the content.
A tag that does not appear in this table is emitted without additional
layout. See also print_html/[1,2]. Please consult the
library source for examples.
\end{description}
\subsubsection{Examples}
In the following example we will generate a table of Prolog predicates
we find from the SWI-Prolog help system based on a keyword. The primary
database is defined by the predicate predicate/5 We will make hyperlinks
for the predicates pointing to their documentation.
\begin{code}
html_apropos(Kwd) :-
findall(Pred, apropos_predicate(Kwd, Pred), Matches),
phrase(apropos_page(Kwd, Matches), Tokens),
print_html(Tokens).
% emit page with title, header and table of matches
apropos_page(Kwd, Matches) -->
page([ title(['Predicates for ', Kwd])
],
[ h2(align(center),
['Predicates for ', Kwd]),
table([ align(center),
border(1),
width('80%')
],
[ tr([ th('Predicate'),
th('Summary')
])
| \apropos_rows(Matches)
])
]).
% emit the rows for the body of the table.
apropos_rows([]) -->
[].
apropos_rows([pred(Name, Arity, Summary)|T]) -->
html([ tr([ td(\predref(Name/Arity)),
td(em(Summary))
])
]),
apropos_rows(T).
% predref(Name/Arity)
%
% Emit Name/Arity as a hyperlink to
%
% /cgi-bin/plman?name=Name&arity=Arity
%
% we must do form-encoding for the name as it may contain illegal
% characters. www_form_encode/2 is defined in library(url).
predref(Name/Arity) -->
{ www_form_encode(Name, Encoded),
sformat(Href, '/cgi-bin/plman?name=~w&arity=~w',
[Encoded, Arity])
},
html(a(href(Href), [Name, /, Arity])).
% Find predicates from a keyword. '$apropos_match' is an internal
% undocumented predicate.
apropos_predicate(Pattern, pred(Name, Arity, Summary)) :-
predicate(Name, Arity, Summary, _, _),
( '$apropos_match'(Pattern, Name)
-> true
; '$apropos_match'(Pattern, Summary)
).
\end{code}
\subsubsection{Remarks on the \pllib{http/html_write} library}
This library is the result of various attempts to reach at a more
satisfactory and Prolog-minded way to produce HTML text from a program.
We have been using Prolog for the generation of web pages in a number of
projects. Just using format/2 never was a real
option, generating error-prone HTML from clumsy syntax. We started
with a layer on top of format/2, keeping track of the current nesting
and thus always capable of properly closing the environment.
DCG based translation however naturally exploits Prolog's term-rewriting
primitives. If generation fails for whatever reason it is easy to
produce an alternative document (for example holding an error message).
The approach presented in this library has been used in combination with
\pllib{http/httpd} in three projects: viewing RDF in a browser,
selecting fragments from an analysed document and presenting parts of
the XPCE documentation using a browser. It has proven to be
able to deal with generating pages quickly and comfortably.
In a future version we will probably define a goal_expansion/2 to do
compile-time optimisation of the library. Quotation of known text and
invocation of sub-rules using the \bsl\arg{RuleSet} and
<Module>:<RuleSet> operators are costly operations in the analysis
that can be done at compile-time.
\input{jswrite}
\input{httppath}
\input{htmlhead}
\input{httppwp}
\subsection{Security}
Writing servers is an inherently dangerous job that should be carried out
with some considerations. You have basically started a program on a
public terminal and invited strangers to use it. When using the
interactive server or inetd based server the server runs under your
privileges. Using CGI scripted it runs with the privileges of your
web-server. Though it should not be possible to fatally compromise a
Unix machine using user privileges, getting unconstrained access to the
system is highly undesirable.
Symbolic languages have an additional handicap in their inherent
possibilities to modify the running program and dynamically create goals
(this also applies to the popular perl and java scripting languages).
Here are some guidelines.
\begin{itemlist}
\item [Check your input]
Hardly anything can go wrong if you check the validity of
query-arguments before formulating an answer.
\item [Check filenames]
If part of the query consists of filenames or directories, check
them. This also applies to files you only read. Passing names as
\file{/etc/passwd}, but also \file{../../../../../etc/passwd} are
tried by experienced hackers to learn about the system they want
to attack. So, expand provided names using absolute_file_name/[2,3]
and verify they are inside a folder reserved for the server. Avoid
symbolic links from this subtree to the outside world. The example
below checks validity of filenames. The first call ensures proper
canonisation of the paths to avoid an mismatch due to
symbolic links or other filesystem ambiguities.
\begin{code}
check_file(File) :-
absolute_file_name('/path/to/reserved/area', Reserved),
absolute_file_name(File, Tried),
atom_concat(Reserved, _, Tried).
\end{code}
\item [Check scripts]
Should input in any way activate external scripts using shell/1
or \exam{open(pipe(Command), ...)}, verify the argument once more.
\item [Check meta-calling]
\emph{The} attractive situation for you and your attacker is below:
\begin{code}
reply(Query) :-
member(search(Args), Query),
member(action=Action, Query),
member(arg=Arg, Query),
call(Action, Arg). % NEVER EVER DO THIS!
\end{code}
All your attacker has to do is specify \arg{Action} as \const{shell}
and \arg{Arg} as \const{/bin/sh} and he has an uncontrolled shell!
\end{itemlist}
\subsection{Tips and tricks}
\begin{itemlist}
\item [URL Locations]
With an application in mind, it is tempting to make all URL
locations short and directly connected to the root (\const{/}). This is
\emph{not} a good idea. It is adviced to have all locations in a server
below a directory with an informative name. Consider to make the root
location something that can be changed using a global setting.
\begin{itemize}
\item Page generating code can easily be reused. Using locations
directly below the root however increases the likelihood
of conflicts.
\item Multiple servers can be placed behind the same public
server as explained in \secref{proxy}. Using a common
and fairly unique root, redirection is much easier and
less likely to lead to conflicts.
\end{itemize}
\item [Debugging]
Please check the section \url[``Thread-support
library(threadutil)'']{http://gollem.science.uva.nl/SWI-Prolog/Manual/thutil.html}
of the SWI-Prolog reference manual.
\end{itemlist}
\section{Transfer encodings}
\label{sec:transfer}
\index{chunked,encoding}%
\index{deflate,encoding}%
The HTTP protocol provides for \jargon{transfer encodings}. These define
filters applied to the data described by the \const{Content-type}. The
two most popular transfer encodings are \const{chunked} and
\const{deflate}. The \const{chunked} encoding avoids the need for
a \const{Content-length} header, sending the data in chunks, each of
which is preceded by a length. The \const{deflate} encoding provides
compression.
Transfer-encodings are supported by filters defined as foreign libraries
that realise an encoding/decoding stream on top of another stream.
Currently there are two such libraries: \pllib{http/http_chunked.pl}
and \pllib{zlib.pl}.
There is an emerging hook interface dealing with transfer encodings. The
\pllib{http/http_chunked.pl} provides a hook used by
\pllib{http/http_open.pl} to support chunked encoding in http_open/3.
Note that both \file{http_open.pl} \emph{and} \file{http_chunked.pl}
must be loaded for http_open/3 to support chunked encoding.
\subsection{The \pllib{http/http_chunked} library}
\begin{description}
\predicate{http_chunked_open}{3}{+RawStream, -DataStream, +Options}
Create a stream to realise HTTP chunked encoding or decoding. The
technique is similar to library(zlib), using a Prolog stream as a filter
on another stream. See online documentation at
\url{http://gollem.science.uva.nl/SWI-Prolog/pldoc/} for details.
\end{description}
\input{json.tex}
\section{Status}
The SWI-Prolog HTTP library is in active use in a large number of
projects. It is considered one of the SWI-Prolog core libraries that is
actively maintained and regularly extended with new features. This is
particularly true for the multi-threaded server. The XPCE and inetd based
servers are not widely used.
This library is by no means complete and you are free to extend it.
\printindex
\end{document}