Skip to content

Instantly share code, notes, and snippets.

@JanWielemaker
Created November 14, 2019 10:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JanWielemaker/9b0661fa23a5298ba600428b2389dfa9 to your computer and use it in GitHub Desktop.
Save JanWielemaker/9b0661fa23a5298ba600428b2389dfa9 to your computer and use it in GitHub Desktop.
A statefull HTTP server
:- module(http_statefull_server,
[ server/0
]).
:- use_module(library(http/http_server)).
:- use_module(library(http/http_session)).
:- use_module(library(broadcast)).
:- use_module(library(modules)).
:- use_module(library(debug)).
:- http_handler(root(.), http_redirect(moved, root(home)), []).
:- http_handler(root(home), home, []).
:- debug(http(engine)).
%! server
%
% Start the HTTP server at port 5050
server :-
http_server([port(5050)]).
%! session_event(+Event, +Peer)
%
% Trap session creation and destruction to create and destroy the
% engine that will provide the state for this session.
:- listen(http_session(Action),
session_event(Action)).
session_event(begin(SessionID, _Peer)) :-
engine_create(_, session_setup(SessionID), Engine),
http_session_asserta(engine(Engine)),
debug(http(engine), 'Created engine ~p for session ~p',
[Engine, SessionID]).
session_event(end(SessionID, _Peer)) :-
http_session_data(engine(Engine), SessionID),
debug(http(engine), 'Destroy engine ~p for session ~p',
[Engine, _0SessionID]),
engine_post(Engine, quit, _),
engine_destroy(Engine).
session_setup(SessionID) :-
in_temporary_module(_Module, true, session_setup_2(SessionID)).
%! session_module(-Module)
%
% Provides a module that is private to the session engine.
% Alternatively, state can be maintained in _thread_local_ predicates.
:- thread_local
session_module/1.
:- meta_predicate
session_setup_2(:).
session_setup_2(M:_SessionID) :-
debug(http(engine), 'Using private module ~p', [M]),
asserta(session_module(M)),
set_prolog_flag(M:unknown, fail),
session_main.
session_main :-
engine_fetch(Command),
( Command == quit
-> engine_yield(bye)
; Command = run(Goal, Vars)
-> ( catch(Goal, E, true)
-> ( var(E)
-> engine_yield(true(Vars))
; engine_yield(exception(E))
)
; engine_yield(false)
),
garbage_collect, % make myself small while waiting
session_main
).
:- meta_predicate
in_engine(0).
%! in_engine(:Goal) is semidet.
%
% Run Goal in the state engine as once/1.
in_engine(Goal) :-
http_session_data(engine(E)),
term_variables(Goal, Vars),
engine_post(E, run(Goal,Vars), Result),
( Result = true(Reply)
-> Vars = Reply
; Result = exception(E)
-> throw(E)
).
home(_Request) :-
in_engine(incr(N)),
reply_html_page(
title('State example'),
[ p(['Current state is ', b(N)])
]).
incr(N) :-
session_module(M),
( retract(incr_val(M:N0))
-> N is N0+1
; N = 1
),
assert(incr_val(M:N)).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment