A simple example of a SWI-Prolog server and a Javascript client.
See also: https://www.swi-prolog.org/howto/http/
“We are tied down to a language which makes up in obscurity what it lacks in style.”
— Tom Stoppard, Rosencrantz and Guildenstern are Dead
1995 — Brendan Eich reads up on every mistake ever made in designing a
programming language, invents a few more, and creates
LiveScript. Later, in an effort to cash in on the popularity of Java
the language is renamed JavaScript. Later still, in an effort to cash
in on the popularity of skin diseases the language is renamed
ECMAScript.
— A Brief, Incomplete, and Mostly Wrong History of Programming Languages
This code is an overly simple example of client-server code, intended as a tutorial or to extend the HTTP support documentation in the SWI-Prolog manual and the tutorial at http://www.pathwayslms.com/swipltuts/html.
The example code sends a query to the server, which executes it and returns the result. Obviously, this is a dangerous thing to do without proper sand-boxing, but it's useful for tutorial purposes. (In other words, don't run this server anywhere that someone from the "outside" can access it.)
The code has only been tested with a recent "development" release of SWI-Prolog (8.3.7) on Ubuntu 18.0.4 and with the Chrome browser. The server has been reported to run under Windows 10 from the DOS prompt, with the client on Microsoft Edge browser.
Install SWI-Prolog development version.
For Ubuntu, Debian, and similar (following the instructions at https://www.swi-prolog.org/build/PPA.html), use these steps:
sudo apt install software-properties-common
sudo apt-add-repository ppa:swi-prolog/devel
sudo apt update
sudo apt install swi-prolog
Start the server:
swipl simple_server.pl --port=9999 --staticdir=static
In a browser, start the client: http://localhost:9999.
You might try this query:
get_time(TimeStamp), stamp_date_time(TimeStamp, DateTime, local), bagof(Key-Value, date_time_value(Key, DateTime, Value), DatePieces), dict_create(DateDict, date, DatePieces)
or this:
setof(K:V,current_prolog_flag(K,V), Flags), forall(member(K:V, Flags), format('~|~q:~t~40+~q~n', [K,V]))
To stop the server, enter the line
halt.
or ctrl-D (possibly ctrl-Z on Windows).
Here's an example of a query and response, at the HTTP level, using nc
as the client:
(echo 'POST /json HTTP/1.1';
echo 'Host: localhost:9999';
echo 'Content-Length: 18';
echo 'Content-Type: application/json'
echo 'Accept: */*'
echo 'Origin: http://localhost:9999';
echo ''
echo -n '{"query":"X = 1."}'
) | nc -C -N localhost 9999
which produces this output (from nc
):
HTTP/1.1 200 OK
Date: Wed, 20 Apr 2022 21:01:42 GMT
Connection: Keep-Alive
Content-Type: application/json
Content-Length: 131
{"error":"", "printed_output":"", "query":"X = 1.", "query_after_call":"1=1", "success":true, "vars": [ {"value":"1", "var":"X"} ]}
There are three client components and one server component:
static/simple_client.html
static/simple_client.css
static/simple_client.js
simple_server.pl
If the server is accessed at the top level ("/
"), the http_handler
for root(.)
does a redirect to static('simple_client.html')
, which
causes the browser to fetch that HTML (using the server). All other
static files are accessed by the same mechanism.
-
The HTML path
static('simple_client.html')
is resolved usinghttp:location/2
facts - see the comments there. -
static_dir('simple_client.html')
is resolved using file_search_path/2. During server start-up,assert_server_locations/1
is called to asserta a file_search_path fact to redirectstatic(Path)
to the directory specified by the option--staticdir
).
The simple_client.html
file has this line in its <head>
:
<script src="simple_client.js" defer="defer"></script>
which contains a function renderPage()
, which is invoked by:
<body onload="renderPage();">
(The simple_client.js
code is fetched from the server, using the
http_handler/3
for static(.)
, which in turn uses
http_reply_from_files/2
(in library(http/http_files)
to do the
work.)
The JavaScript code in the client communicates with the server using
the fetchFromServer
function, which sends a stringified JSON data
structure containing the request and sets a callback for processing
the result (which is also a JSON-encoded data structure).
On the server, the http_handler
for root(json)
(/json
) calls
reply_with_json/1
, which deserializes the JSON request and calls
json_response/2
to deal with the specific request.
The favico.ico
is taken from http://swi-prolog.org.