Most code doesn't need to use this directly; instead use
library(http/http_server), which combines this library with the
typical HTTP libraries that most servers need.
This library defines the HTTP server frontend of choice for SWI-Prolog.
It is based on the multi-threading capabilities of SWI-Prolog and thus
exploits multiple cores to serve requests concurrently. The server
scales well and can cooperate with library(thread_pool) to control the
number of concurrent requests of a given type. For example, it can be
configured to handle 200 file download requests concurrently, 2 requests
that potentially uses a lot of memory and 8 requests that use a lot of
On Unix systems, this library can be combined with
library(http/http_unix_daemon) to realise a proper Unix service process
that creates a web server at port 80, runs under a specific account,
optionally detaches from the controlling terminal, etc.
Combined with library(http/http_ssl_plugin) from the SSL package, this
library can be used to create an HTTPS server. See
<plbase>/doc/packages/examples/ssl/https for an example server using a
self-signed SSL certificate.
- http_server(:Goal, :Options) is det
- Create a server at Port that calls Goal for each parsed request.
Options provide a list of options. Defined options are
- Port to bind to. Address is either a port or a term
Host:Port. The port may be a variable, causing the system
to select a free port. See tcp_bind/2.
- Instead of binding to a TCP port, bind to a Unix Domain
Socket at Path.
- Affects the message printed while the server is started.
Interpreted as a URI relative to the server root.
- If provided, use this socket instead of the creating one and
binding it to an address. The socket must be bound to an
- Determine the number of worker threads. Default is 5. This
is fine for small scale usage. Public servers typically need
a higher number.
- Maximum time of inactivity trying to read the request after a
connection has been opened. Default is 60 seconds. See
set_stream/1 using the timeout option.
- Time to keep `Keep alive' connections alive. Default is
- Stack limit to use for the workers. The default is inherited
If you need to control resource usage you may consider the
spawn option of http_handler/3 and library(thread_pool).
false), do not print an informational
message that the server was started.
A typical initialization for an HTTP server that uses
http_dispatch/1 to relay requests to predicates is:
Note that multiple servers can coexist in the same Prolog
process. A notable application of this is to have both an HTTP
and HTTPS server, where the HTTP server redirects to the HTTPS
server for handling sensitive requests.
- make_socket(+Address, :OptionsIn, -OptionsOut) is det[private]
- Create the HTTP server socket and worker pool queue. OptionsOut
is quaranteed to hold the option
|OptionsIn||- is qualified to allow passing the
module-sensitive ssl option argument.|
- make_addr_atom(+Scheme, +Address, -Atom) is det[private]
- Create an atom that identifies the server's queue and thread
- create_server(:Goal, +Address, +Options) is det[private]
- Create the main server thread that runs accept_server/2 to
listen to new requests.
- http_current_server(:Goal, ?Port) is nondet
- True if Goal is the goal of a server at Port.
- - Use
- http_server_property(?Port, ?Property) is nondet
- True if Property is a property of the HTTP server running at
Port. Defined properties are:
- Goal used to start the server. This is often
- Scheme is one of
- Time-stamp when the server was created.
- http_workers(+Port, -Workers) is det
- http_workers(+Port, +Workers:int) is det
- Query or set the number of workers for the server at this port.
The number of workers is dynamically modified. Setting it to 1
(one) can be used to profile the worker using tprofile/1.
- http_add_worker(+Port, +Options) is det
- Add a new worker to the HTTP server for port Port. Options
overrule the default queue options. The following additional
options are processed:
- The created worker will automatically terminate if there is
no new work within Seconds.
- http_current_worker(?Port, ?ThreadID) is nondet
- True if ThreadID is the identifier of a Prolog thread serving
Port. This predicate is motivated to allow for the use of
arbitrary interaction with the worker thread for development and
- accept_server(:Goal, +Initiator, +Options)[private]
- The goal of a small server-thread accepting new requests and
posting them to the queue of workers.
- Close the server socket.
- http_stop_server(+Port, +Options)
- Stop the indicated HTTP server gracefully. First stops all
workers, then stops the server.
- To be done
- - Realise non-graceful stop
- http_enough_workers(+Queue, +Why, +Peer) is det
- Check that we have enough workers in our queue. If not, call the
hook http:schedule_workers/1 to extend the worker pool. This
predicate can be used by accept_hook/2.
- http:schedule_workers(+Data:dict) is semidet[multifile]
- Hook called if a new connection or a keep-alive connection
cannot be scheduled immediately to a worker. Dict contains the
- Port number that identifies the server.
- One of
accept for a new connection or
keep_alive if a
worker tries to reschedule itself.
- Identify the other end of the connection
- Number of messages waiting in the queue.
- Message queue used to dispatch accepted messages.
Note that, when called with
reason:accept, we are called in
the time critical main accept loop. An implementation of this
hook shall typically send the event to thread dedicated to
dynamic worker-pool management.
- See also
- - http_add_worker/2 may be used to create (temporary) extra
- Create the pool of HTTP worker-threads. Each worker has the
- resize_pool(+Queue, +Workers) is det[private]
- Create or destroy workers. If workers are destroyed, the call
waits until the desired number of waiters is reached.
- Run HTTP worker main loop. Workers simply wait until they are
passed an accepted socket to process a client.
If the message
quit(Sender) is read from the queue, the worker
- open_client(+Message, +Queue, -Goal, -In, -Out, +Options, -ClientOptions) is semidet[private]
- Opens the connection to the client in a worker from the message
sent to the queue by accept_server/2.
- open_client(+Message, +Goal, -In, -Out, -ClientOptions, +Options) is det[private]
- check_keep_alive_connection(+In, +TimeOut, +Peer, +In, +Out) is semidet[private]
- Wait for the client for at most TimeOut seconds. Succeed if the
client starts a new request within this time. Otherwise close
the connection and fail.
- Called when worker is terminated due to http_workers/2 or a
(debugging) exception. In the latter case, recreate_worker/2
creates a new worker.
- recreate_worker(+Status, +Queue) is semidet[private]
- Deal with the possibility that threads are, during development,
killed with abort/0. We recreate the worker to avoid that eventually
we run out of workers. If we are aborted due to a halt/0 call,
thread_create/3 will raise a permission error.
The first clause deals with the possibility that we cannot write to
user_error. This is possible when Prolog is started as a service
using some service managers. Would be nice if we could write an
error, but where?
- thread_httpd:message_level(+Exception, -Level)[multifile]
- Determine the message stream used for exceptions that may occur
during server_loop/5. Being multifile, clauses can be added by the
application to refine error handling. See also message_hook/3 for
further programming error handling.
- Re-queue a connection to the worker pool. This deals with
processing additional requests on keep-alive connections.
- http_process(Message, Queue, +Options)[private]
- Handle a single client message on the given stream.
- Close connection associated to Request. See also http_requeue/1.
- close_connection(+Peer, +In, +Out)[private]
- Closes the connection from the server to the client. Errors are
currently silently ignored.
- http_spawn(:Goal, +Options) is det
- Continue this connection on a new thread. A handler may call
http_spawn/2 to start a new thread that continues processing the
current request using Goal. The original thread returns to the
worker pool for processing new requests. Options are passed to
thread_create/3, except for:
- Interfaces to library(thread_pool), starting the thread
on the given pool.
If a pool does not exist, this predicate calls the multifile
hook create_pool/1 to create it. If this predicate succeeds
the operation is retried.
- Lazy creation of worker-pools for the HTTP server. This
predicate calls the hook create_pool/1. If the hook fails
it creates a default pool of size 10. This should suffice most
typical usecases. Note that we get a permission error if the
pool is already created. We can ignore this.
- thread_repeat_wait(:Goal) is multi[private]
- Acts as
repeat, thread_idle(Goal), choosing whether to use a
short idle time based on the average firing rate.
- new_rate_mma(+N, +Resolution, -State) is det[private]
- update_rate_mma(!State, -MMA) is det[private]
- Implement Modified Moving Average computing the average time
between requests as an exponential moving averate with alpha=1/N.
|Resolution||- is the time resolution in 1/Resolution seconds. All
storage is done in integers to avoid the need for stack freezing in
- See also
- - https://en.wikipedia.org/wiki/Moving_average