Simple Prolog server with JavaScript client

I’ve written a simple sample client server at

This is intended to supplement the existing documentation and tutorial (basically, it has the skeleton server I wrote while puzzling out some details in the documentation).

The documentation is still a bit rough; any suggestions or PRs will be gratefully accepted. And I would also appreciate being told of any design errors.

3 Likes

Nice. :slightly_smiling_face:

I like the use of fetch and addressing CORS.

Thanks for finding all of the debug/1 options, e.g. debug(cookie).

I was planning on trying a callback to the SWI-Prolog server for use with Cytoscape.js so will definitely be looking at your code with a fine tooth comb. Will give feedback as I find it.


The first bit of feedback is the choice of license.

I find that most JavaScript examples have a MIT license (ref) and the SWI-Prolog uses Simplified BSD license (ref)


While the GitHub page notes how to use it with SWI-Prolog on Ubuntu it also works on Windows 10.

Details

From DOS prompt

Microsoft Windows [Version 10.0.19041.450]
(c) 2020 Microsoft Corporation. All rights reserved.

C:\Users\Groot>cd "C:/Users/Groot/Documents/swipl-server-js-client"

C:\Users\Groot\Documents\swipl-server-js-client>swipl simple_server.pl --port=9999 --staticdir=static
% static dir: static
% Started server at http://localhost:9999/
% Starting REPL ...
?-
% [Thread httpd@9999_5] [1] get / ...
% [Thread httpd@9999_5] [1] 302 moved_temporary(/static/simple_client.html); 394 bytes
% [Thread httpd@9999_1] [2] get /static/simple_client.html ...
% [Thread httpd@9999_1] [2] 200 file(text/html,c:/users/groot/documents/swipl-server-js-client/static/simple_client.html); 2,084 bytes
% [Thread httpd@9999_2] [3] get /static/simple_client.css ...
% [Thread httpd@9999_2] [3] 200 file(text/css,c:/users/groot/documents/swipl-server-js-client/static/simple_client.css); 40 bytes
% [Thread httpd@9999_2] [4] get /static/simple_client.js ...
% [Thread httpd@9999_2] [4] 200 file(text/javascript,c:/users/groot/documents/swipl-server-js-client/static/simple_client.js); 4,161 bytes
% [Thread httpd@9999_2] [5] get /static/favicon.ico ...
% [Thread httpd@9999_2] [5] 200 file(image/x-ico,c:/users/groot/documents/swipl-server-js-client/static/favicon.ico); 32,038 bytes
% [Thread httpd@9999_5] [6] post /json ...
% [Thread httpd@9999_5] [6] 200 OK (0.016 seconds; 20,168 bytes)
% [Thread httpd@9999_4] [7] post /json ...
% [Thread httpd@9999_4] [7] 200 OK (0.000 seconds; 1,811 bytes)
% [Thread httpd@9999_1] [8] post /json ...
% [Thread httpd@9999_1] [8] 200 OK (0.000 seconds; 20,168 bytes)

Using Microsoft Edge


For those looking to start the code from the Prolog top level, so they can use make/0 and such, here is a modified predicate

Details
simple_server_main(Opts0) :-
    dict_create(Opts, opts, Opts0),
    assert_server_locations(Opts),
    http_server([port(Opts.port), workers(5)]),
    debug(log, 'Starting REPL ...', []),
    prolog.  % REPL - terminated with exit.

Don’t forget to export the predicate:

:- module(simple_server,[
        simple_server_main/0,
        simple_server_main/1,
        simple_server_main2/0
    ]).

Example run:

Welcome to SWI-Prolog (threaded, 64 bits, version 8.3.7)

?- working_directory(_,'C:/Users/Groot/Documents/swipl-server-js-client').
true.

?- [simple_server].
true.

?- simple_server_main([port(9999),staticdir(static)]).
% static dir: static
% Started server at http://localhost:9999/
% Starting REPL ...
% Break level 1
[1]  ?- 
% [Thread httpd@9999_3] [1] get /static/simple_client.html ...
% [Thread httpd@9999_3] [1] 304 not_modified; 0 bytes
% [Thread httpd@9999_3] [2] get /static/favicon.ico ...
% [Thread httpd@9999_3] [2] 200 file(image/x-ico,c:/users/groot/documents//swipl-server-js-client/static/favicon.ico); 32,038 bytes
% [Thread httpd@9999_4] [3] post /json ...
% [Thread httpd@9999_4] [3] 200 OK (0.000 seconds; 141 bytes)

Thanks for this code! A simple self-contained SWI-Prolog HTTP API example has been asked many times.

i have installed it, I appreciate it a lot. This library: http/authenticate and the library: persistent should be added to be able to do the most simple and secure pop-up http authentication and to save the additional user-data data in the persistent prolog db. the main draw back of using ODBC with a database is that many odbc tools do not have the database Create option ( has to be created by db manager tools )

1 Like

I don’t want to complicate my example with non-essential things. But if the authentication and persistence stuff can be added as a separate piece (e.g., an additional handler, or a single line added to an existing handler), then please send a PR or diff.

(I didn’t include SSL support because I gave up after 5 minutes and I don’t need it right now. If someone has a simple “how to” for that, I’d like to add SSL.)

1 Like

i can show you the SSL example because i have 1 linux server that is running with swi-prolog and ssl succesfully for a few months. it is ridiculously simple, it is only 1 line which calls the sll with 2 key files, this the swi-prolog code which i use and which is running :

server(Port) :-
   http_server(http_dispatch, [port(Port),
      ssl([
         certificate_file('/etc/apache2/certificate/certificate.crt'),
         key_file('/etc/apache2/certificate/private.key')
      ])
   ]).

you can obtain the key files here: https://www.sslforfree.com

2 Likes

My problem is that I don’t know how to create the certificates, and my attempt to follow the documentation failed (this was a few months ago, so I forget where I found the documentation or what commands I tried). On Linux, of course. And I don’t have a specific domain to secure; I just wanted to run SSL to http://localhost for testing.

The ones that Discourse.com uses are from Let’s Encrypt and they can be configured for automatic renewal. They also have a Discourse forum for Q&A.

No promises, but I will take a look at this in the next few days as I too would like to see this work. :slightly_smiling_face:


EDIT

As I don’t have a Mac I will not be able to do such.
I can to Windows and Linux via WSL 2.

Personal notes

How to test secure (https) services from localhost
mkcert is a simple tool for making locally-trusted development certificates. It requires no configuration.
How to add a trusted CA certificate to Chrome and Firefox
StackOverflow Q&A: Getting Chrome to accept self-signed localhost certificate

1 Like

I’ve added Issues for changing the license and adding SSL.

Please file additional issues at https://github.com/kamahen/swipl-server-js-client/issues

1 Like

I have tried too in the past several times to make ssl operational on Localhost, I didnt succeed in this. After that i made de javascript conditional so that on localhost it reads everything from another port, javascript can check wether you are on localhost or not.

To make ssl working on localhost you need to map 127.0.0.1 to some non-“localhost” domain. For example, if your live environment is example.com, then just add to /etc/hosts:

127.0.0.1 example.com

and use your live cert files.

After you finish development, you can comment out the line (add # to the beginning).

ok that is a great solution if it works, i will certainly try this

You can also simply make a self-signed certificate. The only drawback is that the client you want to use must be told to accept this. Some browsers make this pretty hard. On Firefox you get some seriously looking warnings, but you can quite easily say you know what you are doing.

I’ve changed the license to simplified BSD - I presume there’s no need to mention MIT. (I was just copying another license I had used, for some different software).

If I’ve done anything wrong, please re-open the issue at github.

1 Like

I noticed in the code that the swi-server is called with worker(5)

I have another swi- webserver already running which doesnt use this worker(5) code,

my question is: does that mean that without the workers specified, the server operates such that every web-request is only handled when the previous request is succesfully terminated?

so with worker(5) the server will have multi threading, so that it handles several requests simultaniously?

I think the default is 4 workers. If you use library(http/http_dyn_workers), default loaded by library(http/http_server) it will dynamically add new workers if there are requests waiting and terminate workers that have been idle too long, respecting certain scheduling limitations. See the library for details.

And yes, by default it handles requests simultaneously. You can prevent that by setting the workers to 1 and not loading library(http/http_server) or disable this by setting http:max_workers to 1.

thankyou for the information

I’ll remove the workers(5) option. It’s not special, compared to the other options. :wink:

to run the swi http server in general, in old posts it was stated
that one could use:

:- use_module(library(http/thread_httpd)).
:- use_module(library(http/http_dispatch)).

to start the server

in the recent posts I see that

:- use_module(library(http/http_server)).

was the way to do it, are these 2 exchangable?

or is the new approach better? the last approach was recommended in the recent posts form this subject

After importing the same set of several hard to remember library names over and over and considering the fact that these libraries export several deprecated predicates or predicates that are not even intended for end users I have decided to add library(http/http_server). It doesn’t do much: just load all the core components of the HTTP server infrastructure and export the stuff that is still considered relevant.

Better? That depends on your perspective. I guess if you just want a standard server the answer should be “yes”. If you want more control (aggressively minimize the footprint, replace components, etc.) the answer is probably “no”.

3 Likes