1838 lines
68 KiB
Plaintext
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}
|
||
|
|