-
-
Save leobm/255fa8ff366bf87106bc5cf147e6a6c4 to your computer and use it in GitHub Desktop.
SWI-Prolog echo server with some JSON manipulation using websockets.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
% INSTRUCTIONS | |
% =swipl echo-server.pl= | |
% =:- start_server.= | |
% | |
% Then navigate to http://localhost:3000 in your browser | |
:- module(echo_server, | |
[ start_server/0, | |
stop_server/0 | |
] | |
). | |
:- use_module(library(http/thread_httpd)). | |
:- use_module(library(http/http_dispatch)). | |
:- use_module(library(http/http_files)). | |
:- use_module(library(http/websocket)). | |
% http_handler docs: http://www.swi-prolog.org/pldoc/man?predicate=http_handler/3 | |
% =http_handler(+Path, :Closure, +Options)= | |
% | |
% * root(.) indicates we're matching the root URL | |
% * We create a closure using =http_reply_from_files= to serve up files | |
% in the local directory | |
% * The option =spawn= is used to spawn a thread to handle each new | |
% request (not strictly necessary, but otherwise we can only handle one | |
% client at a time since echo will block the thread) | |
:- http_handler(root(.), | |
http_reply_from_files('.', []), | |
[prefix]). | |
% * root(echo) indicates we're matching the echo path on the URL e.g. | |
% localhost:3000/echo of the server | |
% * We create a closure using =http_upgrade_to_websocket= | |
% * The option =spawn= is used to spawn a thread to handle each new | |
% request (not strictly necessary, but otherwise we can only handle one | |
% client at a time since echo will block the thread) | |
:- http_handler(root(echo), | |
http_upgrade_to_websocket(echo, []), | |
[spawn([])]). | |
start_server :- | |
default_port(Port), | |
start_server(Port). | |
start_server(Port) :- | |
http_server(http_dispatch, [port(Port)]). | |
stop_server() :- | |
default_port(Port), | |
stop_server(Port). | |
stop_server(Port) :- | |
http_stop_server(Port, []). | |
default_port(3000). | |
%! echo(+WebSocket) is nondet. | |
% This predicate is used to read in a message via websockets and echo it | |
% back to the client | |
echo(WebSocket) :- | |
ws_receive(WebSocket, Message, [format(json)]), | |
( Message.opcode == close | |
-> true | |
; get_response(Message.data, Response), | |
write("Response: "), writeln(Response), | |
ws_send(WebSocket, json(Response)), | |
echo(WebSocket) | |
). | |
%! get_response(+Message, -Response) is det. | |
% Pull the message content out of the JSON converted to a prolog dict | |
% then add the current time, then pass it back up to be sent to the | |
% client | |
get_response(Message, Response) :- | |
get_time(Time), | |
Response = _{message:Message.message, time: Time}. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.container { | |
width: 960px; | |
margin: auto; | |
} | |
.message > div { | |
margin-top: 1px; | |
margin-bottom: 1px; | |
border: 2px; | |
border-radius: 10px; | |
padding: 5px; | |
} | |
.message > .content { | |
display: inline-block; | |
background-color: #e5c2c0; | |
} | |
.message > .timestamp { | |
display: inline-block; | |
background-color: #0d5d56; | |
color: #fff; | |
margin-right: 10px; | |
} | |
#message-panel { | |
position: fixed; | |
left: 0px; | |
bottom: 0px; | |
width: 100%; | |
height: 30px; | |
padding-left: 50px; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const WS_PROTO = "ws://" | |
const WS_ROUTE = "/echo" | |
function log(topic, message) { | |
console.log('[' + topic + '] ' + message) | |
} | |
function wsMessageHandler(event) { | |
const payload = JSON.parse(event.data) | |
log("WS Response", "Received message: '" + event.data + "'") | |
const messages = document.getElementById("messages") | |
const message = document.createElement("div") | |
message.className = 'message' | |
const contentElement = document.createElement("div") | |
contentElement.className = 'content' | |
contentElement.appendChild(document.createTextNode(payload.message)) | |
const timestampElement = document.createElement("div") | |
timestampElement.className = 'timestamp' | |
timestampElement.appendChild(document.createTextNode(new Date(payload.time*1000))) | |
message.appendChild(timestampElement) | |
message.appendChild(contentElement) | |
let child = messages.appendChild(message) | |
child.scrollIntoView() | |
} | |
function sendMessage(connection, message) { | |
log("Client", "sending message \"" + message + "\"") | |
connection.send(message) | |
} | |
function openWebSocket() { | |
connection = new WebSocket(WS_PROTO + window.location.host + WS_ROUTE) | |
connection.onerror = (error) => { | |
log("WS", error) | |
} | |
connection.onmessage = wsMessageHandler | |
return connection | |
} | |
document.addEventListener('DOMContentLoaded', (e) => { | |
const input_box = document.getElementById("input-message") | |
const input_button = document.getElementById("message-submit") | |
const connection = openWebSocket() | |
input_button.addEventListener("click", (event) => { | |
const payload = { | |
message: input_box.value | |
} | |
sendMessage(connection, JSON.stringify(payload)) | |
}) | |
log("OnLoad", "Add event listeners") | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>SWI-Prolog echo websockets example</title> | |
<link rel="stylesheet" href="echo.css" type="text/css" media="screen" charset="utf-8"> | |
<script charset="utf-8" src="echo.js"></script> | |
</head> | |
<body> | |
<div class="container"> | |
<div id="messages"></div> | |
<div id="message-panel"> | |
<input id="input-message" type="text"> | |
<button id="message-submit" type="button">Submit</button> | |
</div> | |
</div> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment