Created
November 14, 2019 10:51
-
-
Save JanWielemaker/9b0661fa23a5298ba600428b2389dfa9 to your computer and use it in GitHub Desktop.
A statefull HTTP server
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
:- 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