http package (only partially working)
This commit is contained in:
68
packages/http/examples/README
Normal file
68
packages/http/examples/README
Normal file
@@ -0,0 +1,68 @@
|
||||
This is a simple demo of the HTTP server facilities, providing a simple
|
||||
body and the three documented server instantiations.
|
||||
|
||||
---+ The server main programs are:
|
||||
|
||||
$ demo_threads.pl :
|
||||
Run threaded server. Requires SWI-Prolog with thread-support.
|
||||
The server is started at port 3000 using server/0. server/2
|
||||
allows to specify options. tm/0 provides a graphical display
|
||||
of the runing threads. See source-file.
|
||||
|
||||
$ demo_xpce.pl :
|
||||
Run XPCE-based event-driven server. Requires XPCE. Use
|
||||
?- server(3000). to start the server at port 3000.
|
||||
|
||||
$ demo_inetd :
|
||||
To install this, adjust the first line of this file to point
|
||||
to the installed Prolog executable and add the following line
|
||||
to /etc/inetd.conf (adjust as needed):
|
||||
|
||||
4001 stream tcp nowait nobody /usr/sbin/tcpd /usr/lib/pl-5.1.4/library/http/demo/demo_inetd
|
||||
|
||||
---+ Session management demo:
|
||||
|
||||
$ calc.pl :
|
||||
Multi-threaded server with session management using the
|
||||
html_write.pl library. See source for usage.
|
||||
|
||||
---+ File serving demo:
|
||||
|
||||
$ demo_files.pl :
|
||||
Is a multi-threaded server that serves static files and
|
||||
directory indices.
|
||||
|
||||
---+ Client demo
|
||||
|
||||
$ demo_client.pl :
|
||||
Simple multi-threaded client to test the server under
|
||||
different conditions. Requires SWI-Prolog with thread-support.
|
||||
See source for usage.
|
||||
|
||||
---+ Performance testing
|
||||
|
||||
A very early start of some routines to validate the server platform.
|
||||
Eventually, stress_server.pl will serve different tests from multiple
|
||||
locations and stress_client.pl will contain client code to run
|
||||
individual tests as well as doing multi-threaded tests.
|
||||
|
||||
$ stress_server.pl :
|
||||
Server platform.
|
||||
|
||||
$ stress_client.pl :
|
||||
Client.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
92
packages/http/examples/calc.pl
Normal file
92
packages/http/examples/calc.pl
Normal file
@@ -0,0 +1,92 @@
|
||||
/* $Id$
|
||||
|
||||
Part of SWI-Prolog
|
||||
|
||||
Author: Jan Wielemaker
|
||||
E-mail: wielemak@science.uva.nl
|
||||
WWW: http://www.swi-prolog.org
|
||||
Copyright (C): Public domain
|
||||
*/
|
||||
|
||||
:- use_module(library('http/thread_httpd')).
|
||||
:- use_module(library('http/html_write')).
|
||||
:- use_module(library('http/http_session')).
|
||||
:- use_module(library('http/http_error')).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
This demo shows session state management in a very simple calculator
|
||||
package. It also demonstrates the use of the html_write library. To use
|
||||
it, start Prolog, load this file and run
|
||||
|
||||
?- server.
|
||||
|
||||
Now direct your browser to http://localhost:3000/
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
server :-
|
||||
server(3000, []).
|
||||
|
||||
server(Port, Options) :-
|
||||
http_server(reply,
|
||||
[ port(Port),
|
||||
timeout(20)
|
||||
| Options
|
||||
]).
|
||||
|
||||
reply(Request) :-
|
||||
memberchk(path(Path), Request),
|
||||
reply(Path, Request).
|
||||
|
||||
reply(/, _Request) :-
|
||||
http_session_retractall(formula(_)),
|
||||
Formula = 0,
|
||||
http_session_assert(formula(Formula)),
|
||||
page(Formula).
|
||||
|
||||
reply('/calc', Request) :-
|
||||
memberchk(search(Search), Request),
|
||||
memberchk(operation=Op, Search),
|
||||
memberchk(value=AtomVal, Search),
|
||||
atom_number(AtomVal, Val),
|
||||
http_session_retract(formula(Formula0)),
|
||||
debug(calc, 'Formula0 = ~w', [Formula0]),
|
||||
Formula =.. [Op, Formula0, Val],
|
||||
http_session_assert(formula(Formula)),
|
||||
page(Formula).
|
||||
|
||||
|
||||
page(Formula) :-
|
||||
reply_page('HTTP Session DEMO',
|
||||
[ h2('Simple session demo'),
|
||||
form([ action('/calc'),
|
||||
method('GET')
|
||||
],
|
||||
table([align(center), border(1)],
|
||||
[ tr(td(\formula(Formula))),
|
||||
tr(td([ \ops,
|
||||
input([ name(value) ]),
|
||||
input([ type(submit),
|
||||
value('Calc!')
|
||||
])
|
||||
]))
|
||||
]))
|
||||
]).
|
||||
|
||||
formula(Formula) -->
|
||||
{ sformat(S, '~w', [Formula]),
|
||||
Value is Formula
|
||||
},
|
||||
html([ S, ' = ', Value ]).
|
||||
|
||||
ops -->
|
||||
html(select(name(operation),
|
||||
[ option([selected], +),
|
||||
option([], -),
|
||||
option([], /),
|
||||
option([], *)
|
||||
])).
|
||||
|
||||
reply_page(Title, Content) :-
|
||||
phrase(page(title(Title), Content), HTML),
|
||||
format('Content-type: text/html~n~n'),
|
||||
print_html(HTML).
|
212
packages/http/examples/demo_body.pl
Normal file
212
packages/http/examples/demo_body.pl
Normal file
@@ -0,0 +1,212 @@
|
||||
/* $Id$
|
||||
|
||||
Part of SWI-Prolog
|
||||
|
||||
Author: Jan Wielemaker
|
||||
E-mail: jan@swi.psy.uva.nl
|
||||
WWW: http://www.swi-prolog.org
|
||||
Copyright (C): 1985-2002, University of Amsterdam
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
As a special exception, if you link this library with other files,
|
||||
compiled with a Free Software compiler, to produce an executable, this
|
||||
library does not by itself cause the resulting executable to be covered
|
||||
by the GNU General Public License. This exception does not however
|
||||
invalidate any other reasons why the executable file might be covered by
|
||||
the GNU General Public License.
|
||||
*/
|
||||
|
||||
:- module(demo_body,
|
||||
[ reply/1
|
||||
]).
|
||||
:- use_module(library('http/http_client')).
|
||||
:- use_module(library('http/http_mime_plugin')). % Decode multipart data
|
||||
:- use_module(library('http/http_image')). % make XPCE generate images
|
||||
|
||||
|
||||
:- style_check(-atom). % allow long atoms
|
||||
|
||||
reply(_) :-
|
||||
flag(request, N, N+1),
|
||||
fail.
|
||||
|
||||
% /quit
|
||||
%
|
||||
% Explicitely close the connection
|
||||
|
||||
reply(Request) :-
|
||||
member(path('/quit'), Request), !,
|
||||
format('Connection: close~n', []),
|
||||
format('Content-type: text/html~n~n', []),
|
||||
format('Bye Bye~n').
|
||||
|
||||
% /xpce?class=box
|
||||
%
|
||||
% Make XPCE reply with a graphics image. The demo-body pce_reply/1
|
||||
% is called embedded in a message to XPCE to force the XPCE
|
||||
% incremental garbage collector to reclaim objects created while
|
||||
% serving the request. pce_reply/1 replies to ?class=box using a
|
||||
% blue box with rounded corners.
|
||||
|
||||
reply(Request) :-
|
||||
member(path('/xpce'), Request), !,
|
||||
send(@prolog, call, demo_body:pce_reply(Request)).
|
||||
|
||||
% /env
|
||||
%
|
||||
% Reply with the output of printenv (Unix systems only).
|
||||
|
||||
reply(Request) :-
|
||||
member(path('/env'), Request), !,
|
||||
expand_file_name(~, Home),
|
||||
format('Content-type: text/html~n~n', []),
|
||||
format('<html>~n', []),
|
||||
flag(request, RN, RN),
|
||||
format('Request ~d~n', [RN]),
|
||||
format('<pre>~n', []),
|
||||
format('HOME = ~w~n~n', [Home]),
|
||||
open(pipe(printenv), read, Fd),
|
||||
copy_stream_data(Fd, current_output),
|
||||
close(Fd),
|
||||
format('</pre>~n', []),
|
||||
format('</html>~n', []).
|
||||
|
||||
% /upload
|
||||
% /upload_reply
|
||||
%
|
||||
% Provide a form for uploading a file, and deal with the resulting
|
||||
% upload. Contributed by Nicos Angelopoulos.
|
||||
|
||||
reply(Request) :-
|
||||
member(path('/upload'), Request), !,
|
||||
format('Content-type: text/html~n~n', []),
|
||||
format('<html>~n', []),
|
||||
format('<form action="/upload_reply" enctype="multipart/form-data" method="post">~n', []),
|
||||
format('<input type="file" name="datafile">'),
|
||||
format('<input type="submit" name="sent">'),
|
||||
format('</body>~n', []),
|
||||
format('</html>~n', []).
|
||||
|
||||
reply(Request) :-
|
||||
member(path('/upload_reply'), Request), !,
|
||||
format('Content-type: text/html~n~n', []),
|
||||
format('<html>~n', []),
|
||||
format('<pre>~n', []),
|
||||
write( req(Request) ), nl,
|
||||
http_read_data(Request, Data, []),
|
||||
write( data(Data) ), nl,
|
||||
format('</pre>'),
|
||||
format('</body>~n', []),
|
||||
format('</html>~n', []).
|
||||
|
||||
% /xml
|
||||
%
|
||||
% Return a simple formatted XML message.
|
||||
|
||||
reply(Request) :-
|
||||
member(path('/xml'), Request), !,
|
||||
format('Content-type: text/xml~n~n', []),
|
||||
format('\
|
||||
<message>
|
||||
<head>
|
||||
<from>Jan Wielemaker</from>
|
||||
<to>Prolog users</to>
|
||||
<subject>The SWI-Prolog web-server</subject>
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
This is the first demo of the web-server serving an XML message
|
||||
</p>
|
||||
</body>
|
||||
</message>
|
||||
', []).
|
||||
|
||||
% /foreign
|
||||
%
|
||||
% Test emitting text using UTF-8 encoding
|
||||
|
||||
reply(Request) :-
|
||||
member(path('/foreign'), Request), !,
|
||||
format('Content-type: text/html~n~n', []),
|
||||
format('\
|
||||
<html>
|
||||
<head><title>Foreign characters</title></head>
|
||||
<body>
|
||||
<p>Chinese for book is ~s
|
||||
</body>
|
||||
</html>
|
||||
',
|
||||
[ [23398, 20064]
|
||||
]).
|
||||
|
||||
|
||||
% /work
|
||||
%
|
||||
% Do a lot of work and then say 'ok'. Can be used to test
|
||||
% concurrent access using the multi-threaded server.
|
||||
|
||||
reply(Request) :-
|
||||
member(path('/work'), Request),
|
||||
format(user_error, 'Starting work ...', []),
|
||||
forall(between(1, 10000000, _), atom_codes(_, "hello")),
|
||||
format(user_error, 'done!~n', []),
|
||||
format('Content-type: text/plain~n~n', []),
|
||||
format('ok~n').
|
||||
|
||||
% /error
|
||||
%
|
||||
% Produce an error. Load http_error to see the effect.
|
||||
|
||||
reply(Request) :-
|
||||
member(path('/error'), Request),
|
||||
A is 1/0,
|
||||
format('Content-type: text/plain~n~n', []),
|
||||
format('A = ~w~n', [A]).
|
||||
|
||||
% ... Otherwise
|
||||
%
|
||||
% Print the request itself.
|
||||
|
||||
reply(Request) :-
|
||||
format('Content-type: text/html~n~n', []),
|
||||
format('<html>~n', []),
|
||||
format('<table border=1>~n'),
|
||||
print_request(Request),
|
||||
format('~n</table>~n'),
|
||||
format('</html>~n', []).
|
||||
|
||||
|
||||
print_request([]).
|
||||
print_request([H|T]) :-
|
||||
H =.. [Name, Value],
|
||||
format('<tr><td>~w<td>~w~n', [Name, Value]),
|
||||
print_request(T).
|
||||
|
||||
|
||||
/*******************************
|
||||
* PCE BASED REQUESTS *
|
||||
*******************************/
|
||||
|
||||
pce_reply(Request) :-
|
||||
memberchk(search(Search), Request),
|
||||
memberchk(class=box, Search),
|
||||
new(Box, box(200,200)),
|
||||
send(Box, radius, 20),
|
||||
send(Box, fill_pattern, colour(skyblue)),
|
||||
reply_image(Box, []).
|
||||
|
||||
|
||||
|
161
packages/http/examples/demo_client.pl
Normal file
161
packages/http/examples/demo_client.pl
Normal file
@@ -0,0 +1,161 @@
|
||||
/* $Id$
|
||||
|
||||
Part of SWI-Prolog
|
||||
|
||||
Author: Jan Wielemaker
|
||||
E-mail: jan@swi.psy.uva.nl
|
||||
WWW: http://www.swi-prolog.org
|
||||
Copyright (C): 1985-2002, University of Amsterdam
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
As a special exception, if you link this library with other files,
|
||||
compiled with a Free Software compiler, to produce an executable, this
|
||||
library does not by itself cause the resulting executable to be covered
|
||||
by the GNU General Public License. This exception does not however
|
||||
invalidate any other reasons why the executable file might be covered by
|
||||
the GNU General Public License.
|
||||
*/
|
||||
|
||||
:- use_module(library('http/http_client')).
|
||||
:- use_module(library(debug)).
|
||||
:- use_module(library('http/http_sgml_plugin')).
|
||||
|
||||
|
||||
% stress(+Times, +Threads, +URLOrAlias)
|
||||
%
|
||||
% Typical use: stress(1000, 3, 1): run the test 1000 times with
|
||||
% 3 client threads on the /xml test from demo_body.pl and verify
|
||||
% the parsed result.
|
||||
|
||||
stress(Times, Parallel, Alias) :-
|
||||
answer(Alias, URL, _), !,
|
||||
stress(Times, Parallel, URL).
|
||||
stress(Times, Parallel, URL) :-
|
||||
( pool(pool, _)
|
||||
-> delete_pool(pool)
|
||||
; true
|
||||
),
|
||||
create_pool(pool, Parallel),
|
||||
stress(Times, URL),
|
||||
wait_done(Times),
|
||||
delete_pool(pool).
|
||||
|
||||
wait_done(0) :- !.
|
||||
wait_done(N) :-
|
||||
thread_get_message(done, Result),
|
||||
put(Result), flush,
|
||||
N1 is N - 1,
|
||||
wait_done(N1).
|
||||
|
||||
stress(0, _) :- !.
|
||||
stress(N, URL) :-
|
||||
thread_send_message(pool, stress_url(URL)),
|
||||
NN is N - 1,
|
||||
stress(NN, URL).
|
||||
|
||||
stress_url(URL) :-
|
||||
thread_self(Me),
|
||||
atom_number(N, Me),
|
||||
( catch(http_get(URL, X, [connection(close)]), E, true)
|
||||
-> ( var(E)
|
||||
-> ( answer(_, URL, Correct)
|
||||
-> ( X == Correct
|
||||
-> thread_send_message(done, N)
|
||||
; thread_send_message(done, !)
|
||||
)
|
||||
; thread_send_message(done, ?)
|
||||
)
|
||||
; print_message(error, E),
|
||||
thread_send_message(done, 'E')
|
||||
)
|
||||
; thread_send_message(done, -)
|
||||
).
|
||||
|
||||
:- dynamic
|
||||
pool/2. % name, threads
|
||||
|
||||
create_pool(Name, N) :-
|
||||
message_queue_create(Name),
|
||||
findall(Id, (between(1, N, _),
|
||||
thread_create(worker(Name), Id, [])), Threads),
|
||||
assert(pool(Name, Threads)).
|
||||
|
||||
|
||||
delete_pool(Name) :-
|
||||
pool(Name, Threads),
|
||||
forall(member(_, Threads), thread_send_message(Name, thread_exit(ok))),
|
||||
forall(member(Id, Threads), thread_join(Id, _)),
|
||||
message_queue_destroy(Name),
|
||||
retract(pool(Name, Threads)).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
worker(Queue) :-
|
||||
repeat,
|
||||
thread_get_message(Queue, Goal),
|
||||
( catch(Goal, E, true)
|
||||
-> ( var(E)
|
||||
-> true
|
||||
; print_message(error, E)
|
||||
)
|
||||
; print_message(error, goal_failed(Goal))
|
||||
),
|
||||
fail.
|
||||
|
||||
|
||||
/*******************************
|
||||
* CORRECT ANSWERS *
|
||||
*******************************/
|
||||
|
||||
|
||||
answer(1, 'http://localhost:3000/xml',
|
||||
[ element(message,
|
||||
[],
|
||||
[ '\n ',
|
||||
element(head,
|
||||
[],
|
||||
[ '\n ',
|
||||
element(from,
|
||||
[],
|
||||
[ 'Jan Wielemaker'
|
||||
]),
|
||||
'\n ',
|
||||
element(to,
|
||||
[],
|
||||
[ 'Prolog users'
|
||||
]),
|
||||
'\n ',
|
||||
element(subject,
|
||||
[],
|
||||
[ 'The SWI-Prolog web-server'
|
||||
]),
|
||||
'\n '
|
||||
]),
|
||||
'\n ',
|
||||
element(body,
|
||||
[],
|
||||
[ '\n',
|
||||
element(p,
|
||||
[],
|
||||
[ '\nThis is the first demo of the web-server serving an XML message\n'
|
||||
]),
|
||||
'\n '
|
||||
]),
|
||||
'\n'
|
||||
])
|
||||
]).
|
50
packages/http/examples/demo_files.pl
Normal file
50
packages/http/examples/demo_files.pl
Normal file
@@ -0,0 +1,50 @@
|
||||
:- use_module(library(http/thread_httpd)).
|
||||
:- use_module(library(http/html_write)).
|
||||
:- use_module(library(http/http_dispatch)).
|
||||
:- use_module(library(http/http_dirindex)).
|
||||
|
||||
:- http_handler(root(.), serve_files, [prefix]).
|
||||
|
||||
%% server(+Port, +DirSpec) is det.
|
||||
%
|
||||
% Start the server at port Port, serving directories below
|
||||
% DirSpec. DirSpec may contain ~ and $var.
|
||||
%
|
||||
% This simple example defines a complete web-server for static
|
||||
% pages. Note that more specific handlers than the bave (i.e.
|
||||
% using a longer path) have priority over this handler and can
|
||||
% thus be used to add dynamic parts to your server.
|
||||
|
||||
server(Port, DirSpec) :-
|
||||
expand_file_name(DirSpec, [Dir]),
|
||||
assert(user:file_search_path(document_root, Dir)),
|
||||
http_server(http_dispatch, [port(Port)]).
|
||||
|
||||
%% serve_files(Request)
|
||||
%
|
||||
% Server a file or directory according to the path_info field,
|
||||
% which contains the path *after* the http-location matched by the
|
||||
% handler. If the handler is matched *exactly*, path_info is
|
||||
% missing.
|
||||
%
|
||||
% http_safe_file/1 checks the path for attempts to escape from the
|
||||
% hierarchy defined by the =document_root= alias. We find the path
|
||||
% before calling one of the two reply functions because we want to
|
||||
% know whether we are dealing with a directory or a file. After
|
||||
% that, the path is absolute and we must pass unsafe(true) to
|
||||
% avoid the path-checker in the reply-functions complaining.
|
||||
|
||||
serve_files(Request) :-
|
||||
( memberchk(path_info(PathInfo), Request)
|
||||
-> true
|
||||
; PathInfo = './'
|
||||
),
|
||||
http_safe_file(document_root(PathInfo), []),
|
||||
absolute_file_name(document_root(PathInfo), Path,
|
||||
[ access(read)] ),
|
||||
( exists_directory(Path)
|
||||
-> http_reply_dirindex(Path, [unsafe(true)], Request)
|
||||
; http_reply_file(Path, [unsafe(true)], Request)
|
||||
).
|
||||
|
||||
|
18
packages/http/examples/demo_inetd
Executable file
18
packages/http/examples/demo_inetd
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/pl -t main -q -f
|
||||
/* $Id$
|
||||
|
||||
Part of SWI-Prolog
|
||||
|
||||
Author: Jan Wielemaker
|
||||
E-mail: jan@swi.psy.uva.nl
|
||||
WWW: http://www.swi.psy.uva.nl/projects/SWI-Prolog/
|
||||
Copying: GPL-2. See the file COPYING or http://www.gnu.org
|
||||
|
||||
Copyright (C) 1990-2001 SWI, University of Amsterdam. All rights reserved.
|
||||
*/
|
||||
|
||||
:- use_module(demo_body).
|
||||
:- use_module(library('http/inetd_httpd')).
|
||||
|
||||
main :-
|
||||
http_server(reply, []).
|
173
packages/http/examples/demo_openid.pl
Normal file
173
packages/http/examples/demo_openid.pl
Normal file
@@ -0,0 +1,173 @@
|
||||
/* Part of SWI-Prolog
|
||||
|
||||
Author: Jan Wielemaker
|
||||
E-mail: J.Wielemaker@cs.vu.nl
|
||||
WWW: http://www.swi-prolog.org
|
||||
Copyright (C): 2007-2010, University of Amsterdam,
|
||||
VU University Amsterdam
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
As a special exception, if you link this library with other files,
|
||||
compiled with a Free Software compiler, to produce an executable, this
|
||||
library does not by itself cause the resulting executable to be covered
|
||||
by the GNU General Public License. This exception does not however
|
||||
invalidate any other reasons why the executable file might be covered by
|
||||
the GNU General Public License.
|
||||
*/
|
||||
|
||||
|
||||
:- asserta(file_search_path(library, '..')).
|
||||
|
||||
:- use_module(library(uri)).
|
||||
:- use_module(library(http/http_openid)).
|
||||
:- use_module(library(http/http_host)).
|
||||
:- use_module(library(http/http_dispatch)).
|
||||
:- use_module(library(http/http_path)).
|
||||
:- use_module(library(http/thread_httpd)).
|
||||
:- use_module(library(http/html_write)).
|
||||
:- use_module(library(http/http_error)).
|
||||
|
||||
http:location(openid, root(openid), []).
|
||||
|
||||
:- multifile
|
||||
http_openid:openid_hook/1.
|
||||
|
||||
http_openid:openid_hook(trusted(_OpenID, Server)) :-
|
||||
debug(openid(test), 'Trusting server ~q', [Server]).
|
||||
|
||||
|
||||
%% server
|
||||
%
|
||||
% Create demo server and client. After starting the server,
|
||||
% contact http://localhost:8000/
|
||||
|
||||
server :-
|
||||
debug(openid(_)),
|
||||
Port = 8000,
|
||||
http_server(http_dispatch,
|
||||
[ port(Port)
|
||||
]),
|
||||
debug(openid(test), 'Server started at http://localhost:~w/', [Port]).
|
||||
|
||||
|
||||
assoc :-
|
||||
openid_associate('http://localhost:8000/openid/server', Handle, Assoc),
|
||||
writeln(Handle-Assoc).
|
||||
|
||||
%% secret(+Request) is det.
|
||||
%
|
||||
% Example of a handler that requires an OpenID login. If the user
|
||||
% is not logged it, it will be redirected to the login page, from
|
||||
% there to the OpenID server and back here. All this is completely
|
||||
% transparent to us.
|
||||
|
||||
:- http_handler(root('secret'), secret, []).
|
||||
|
||||
secret(Request) :-
|
||||
openid_user(Request, User, []),
|
||||
reply_html_page(title('Secret'),
|
||||
[ 'You\'ve reached the secret page as user ', %'
|
||||
a(href(User), User)
|
||||
]).
|
||||
|
||||
%% root(+Request).
|
||||
%% allow(+Request).
|
||||
%
|
||||
% Shows an indirect login.
|
||||
|
||||
:- http_handler(root(.), root, []).
|
||||
:- http_handler(root('test/verify'), openid_verify([return_to(allow)]), []).
|
||||
:- http_handler(root('test/allow'), allow, []).
|
||||
|
||||
root(_Request) :-
|
||||
reply_html_page(title('Demo OpenID consumer'),
|
||||
[ h1('OpenID consumer'),
|
||||
form([ name(login),
|
||||
action('/test/verify'),
|
||||
method('GET')
|
||||
],
|
||||
[ div([ 'OpenID: ',
|
||||
input([ name(openid_url),
|
||||
size(40),
|
||||
value('http://localhost:8000/user/bob') % test
|
||||
]),
|
||||
input([type(submit), value('Verify!')])
|
||||
])
|
||||
]),
|
||||
p([ 'Or go directly to the ', a(href=secret, 'secret page') ])
|
||||
]).
|
||||
|
||||
|
||||
allow(Request) :-
|
||||
openid_authenticate(Request, Server, Identity, _ReturnTo),
|
||||
reply_html_page(title('Success'),
|
||||
[ h1('OpenID login succeeded'),
|
||||
p([ 'The OpenID server ',
|
||||
a(href(Server),Server),
|
||||
' verified you as ',
|
||||
a(href(Identity), Identity)
|
||||
])
|
||||
]).
|
||||
|
||||
|
||||
/*******************************
|
||||
* OpenID SERVER *
|
||||
*******************************/
|
||||
|
||||
:- http_handler(root('user/'), user_page, [prefix]).
|
||||
:- http_handler(openid(server), openid_server([]), []).
|
||||
:- http_handler(openid(grant), openid_grant, []).
|
||||
|
||||
:- multifile
|
||||
http_openid:openid_hook/1.
|
||||
|
||||
http_openid:openid_hook(grant(_Request, Options)) :-
|
||||
debug(openid(test), 'Granting access to ~p', [Options]).
|
||||
|
||||
%% user_page(+Request) is det.
|
||||
%
|
||||
% Generate a page for user as /user/<user>.
|
||||
|
||||
user_page(Request) :-
|
||||
http_current_host(Request, Host, Port,
|
||||
[ global(true)
|
||||
]),
|
||||
http_location_by_id(openid_server, ServerLocation),
|
||||
uri_authority_data(host, AComp, Host),
|
||||
uri_authority_data(port, AComp, Port),
|
||||
uri_authority_components(Authority, AComp),
|
||||
uri_data(scheme, Components, http),
|
||||
uri_data(authority, Components, Authority),
|
||||
uri_data(path, Components, ServerLocation),
|
||||
uri_components(OpenIDServer, Components),
|
||||
memberchk(path_info(User), Request),
|
||||
reply_html_page([ link([ rel('openid.server'),
|
||||
href(OpenIDServer)
|
||||
]),
|
||||
title('OpenID page of ~w'-[User])
|
||||
],
|
||||
h1('OpenID page of ~w'-[User])).
|
||||
|
||||
|
||||
/*******************************
|
||||
* DEBUG *
|
||||
*******************************/
|
||||
|
||||
:- http_handler(root(.), print_request, [prefix]).
|
||||
|
||||
print_request(Request) :-
|
||||
format('Content-type: text/plain~n~n'),
|
||||
pp(Request).
|
20
packages/http/examples/demo_pwp.pl
Normal file
20
packages/http/examples/demo_pwp.pl
Normal file
@@ -0,0 +1,20 @@
|
||||
:- use_module(library(http/thread_httpd)).
|
||||
:- use_module(library(http/http_parameters)).
|
||||
:- use_module(library(http/http_dispatch)).
|
||||
:- use_module(library(http/http_error)).
|
||||
:- use_module(library(http/html_write)).
|
||||
:- use_module(library(http/http_pwp)).
|
||||
|
||||
:- prolog_load_context(directory, Dir),
|
||||
asserta(user:file_search_path(http_demo, Dir)).
|
||||
|
||||
user:file_search_path(pwp_demo, http_demo(pwp)).
|
||||
|
||||
:- http_handler(root(.),
|
||||
pwp_handler([path_alias(pwp_demo), view(true)]),
|
||||
[prefix]).
|
||||
|
||||
server(Port) :-
|
||||
http_server(http_dispatch, [port(Port)]).
|
||||
|
||||
|
30
packages/http/examples/demo_threads.pl
Normal file
30
packages/http/examples/demo_threads.pl
Normal file
@@ -0,0 +1,30 @@
|
||||
/* $Id$
|
||||
|
||||
Part of SWI-Prolog
|
||||
|
||||
Author: Jan Wielemaker
|
||||
E-mail: jan@swi.psy.uva.nl
|
||||
WWW: http://www.swi.psy.uva.nl/projects/SWI-Prolog/
|
||||
Copying: GPL-2. See the file COPYING or http://www.gnu.org
|
||||
|
||||
Copyright (C) 1990-2001 SWI, University of Amsterdam. All rights reserved.
|
||||
*/
|
||||
|
||||
:- load_files([ demo_body,
|
||||
library('http/thread_httpd')
|
||||
],
|
||||
[ silent(true)
|
||||
]).
|
||||
|
||||
server :-
|
||||
server(3000, []).
|
||||
|
||||
server(Port, Options) :-
|
||||
http_server(reply,
|
||||
[ port(Port),
|
||||
timeout(20)
|
||||
| Options
|
||||
]).
|
||||
|
||||
tm :-
|
||||
prolog_ide(thread_monitor).
|
40
packages/http/examples/demo_xpce.pl
Normal file
40
packages/http/examples/demo_xpce.pl
Normal file
@@ -0,0 +1,40 @@
|
||||
/* $Id$
|
||||
|
||||
Part of SWI-Prolog
|
||||
|
||||
Author: Jan Wielemaker
|
||||
E-mail: jan@swi.psy.uva.nl
|
||||
WWW: http://www.swi-prolog.org
|
||||
Copyright (C): 1985-2002, University of Amsterdam
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
As a special exception, if you link this library with other files,
|
||||
compiled with a Free Software compiler, to produce an executable, this
|
||||
library does not by itself cause the resulting executable to be covered
|
||||
by the GNU General Public License. This exception does not however
|
||||
invalidate any other reasons why the executable file might be covered by
|
||||
the GNU General Public License.
|
||||
*/
|
||||
|
||||
:- use_module(library('http/xpce_httpd')).
|
||||
:- use_module(demo_body).
|
||||
|
||||
server(Port) :-
|
||||
http_server(reply,
|
||||
[ port(Port)
|
||||
]).
|
||||
|
||||
|
23
packages/http/examples/pwp/context.pwp
Normal file
23
packages/http/examples/pwp/context.pwp
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html xmlns:pwp="http://www.cs.otago.ac.nz/staffpriv/ok/pwp.pl">
|
||||
|
||||
<head>
|
||||
<title>Context variables for PWP scripts</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>This PWP demo lists the context-parameters that are passed into
|
||||
the script.
|
||||
</p>
|
||||
<ul>
|
||||
<li pwp:ask="member(Name=Value, CONTEXT)">
|
||||
<span class=name pwp:use="Name"/>
|
||||
=
|
||||
<span class=value pwp:use="writeq(Value)"/>
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
25
packages/http/examples/pwp/index.pwp
Normal file
25
packages/http/examples/pwp/index.pwp
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html xmlns:pwp="http://www.cs.otago.ac.nz/staffpriv/ok/pwp.pl">
|
||||
|
||||
<head>
|
||||
<title>Index for PWP demos</title>
|
||||
</head>
|
||||
<body pwp:ask="atom_concat(SCRIPT_DIRECTORY, /, Prefix),
|
||||
atom_concat(Prefix, '*.pwp', Pattern),
|
||||
expand_file_name(Pattern, Paths),
|
||||
maplist(atom_concat(Prefix), Files, Paths)">
|
||||
|
||||
<h1>PWP scripts in this directory</h1>
|
||||
|
||||
<ul>
|
||||
<li pwp:ask="member(F, Files), F \== 'index.pwp'">
|
||||
<a pwp:use="F" pwp:att="$" href="$(F)$"/>
|
||||
<a pwp:att="$" href="$(F)$?view=source">source</a>
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
12
packages/http/examples/pwp/pwp1.pwp
Normal file
12
packages/http/examples/pwp/pwp1.pwp
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<html
|
||||
xmlns:pwp="http://www.cs.otago.ac.nz/staffpriv/ok/pwp.pl"
|
||||
pwp:ask = "ensure_loaded(pwp(pwpdb)), once(msg(Greeting))">
|
||||
<head>
|
||||
<title pwp:use="Greeting"/>
|
||||
</head>
|
||||
<body>
|
||||
<p><span pwp:use="Greeting" pwp:tag='-'/></p>
|
||||
</body>
|
||||
</html>
|
10
packages/http/examples/pwp/pwp2.pwp
Normal file
10
packages/http/examples/pwp/pwp2.pwp
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<html
|
||||
xmlns:pwp="http://www.cs.otago.ac.nz/staffpriv/ok/pwp.pl">
|
||||
<head><title>Example 2</title></head>
|
||||
<body pwp:ask="Hello = 'Hello world', A = 20, B = 22">
|
||||
<h1 pwp:use="Hello"/>
|
||||
<p>The answer is <span pwp:tag='-' pwp:use="C" pwp:ask="C is A+B"/>.</p>
|
||||
</body>
|
||||
</html>
|
25
packages/http/examples/pwp/pwp3.pwp
Normal file
25
packages/http/examples/pwp/pwp3.pwp
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<html
|
||||
xmlns:pwp="http://www.cs.otago.ac.nz/staffpriv/ok/pwp.pl"
|
||||
pwp:ask='ensure_loaded(pwp(pwpdb))'>
|
||||
<head>
|
||||
<title>Phone list for Full-Time staff.</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Phone list for Full-Time staff.</h1>
|
||||
<table
|
||||
pwp:ask = "setof(FullName-Phone,
|
||||
N^O^E^(
|
||||
status(N, full_time),
|
||||
staff(N, FullName, O, Phone, E)
|
||||
),
|
||||
Staff_List)">
|
||||
<tr><th>Name</th><th>Phone</th></tr>
|
||||
<tr pwp:ask="member(FullName-Phone, Staff_List)">
|
||||
<td pwp:use="FullName"/>
|
||||
<td pwp:use="Phone"/>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
23
packages/http/examples/pwp/pwp4.pwp
Normal file
23
packages/http/examples/pwp/pwp4.pwp
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<html
|
||||
xmlns:pwp="http://www.cs.otago.ac.nz/staffpriv/ok/pwp.pl"
|
||||
pwp:ask='ensure_loaded(pwp(pwpdb))'>
|
||||
<head>
|
||||
<title>Phone list for Full-Time staff.</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Phone list for Full-Time staff.</h1>
|
||||
<table
|
||||
pwp:ask = "setof(FullName-E_Mail,
|
||||
N^O^P^staff(N, FullName, O, P, E_Mail),
|
||||
Staff_List)">
|
||||
<tr><th>Name</th><th>Address</th></tr>
|
||||
<tr pwp:ask="member(FullName-E_Mail, Staff_List)">
|
||||
<td pwp:use="FullName"/>
|
||||
<td><a pwp:use="E_Mail"
|
||||
pwp:att='$' href="mailto:$(E_Mail)$"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
11
packages/http/examples/pwp/pwp5.pwp
Normal file
11
packages/http/examples/pwp/pwp5.pwp
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<html
|
||||
xmlns:pwp="http://www.cs.otago.ac.nz/staffpriv/ok/pwp.pl">
|
||||
<head><title>$SHELL</title></head>
|
||||
<body>
|
||||
<p pwp:ask="getenv('SHELL', Shell)"
|
||||
>The default shell is <span pwp:tag="-" pwp:use="Shell"/>.</p>
|
||||
<p pwp:ask="\+getenv('SHELL',_)">There is no default shell.</p>
|
||||
</body>
|
||||
</html>
|
3
packages/http/examples/pwp/pwp6.pwp
Normal file
3
packages/http/examples/pwp/pwp6.pwp
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<a>b</a>
|
3
packages/http/examples/pwp/pwp7.pwp
Normal file
3
packages/http/examples/pwp/pwp7.pwp
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<a><b pwp:use='27' pwp:tag='-'/></a>
|
3
packages/http/examples/pwp/pwp8.pwp
Normal file
3
packages/http/examples/pwp/pwp8.pwp
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<a pwp:ask="B=20,C=22"><b pwp:use="B"/><c pwp:use="C"/></a>
|
12
packages/http/examples/pwp/pwpdb.pl
Normal file
12
packages/http/examples/pwp/pwpdb.pl
Normal file
@@ -0,0 +1,12 @@
|
||||
% This is a tiny data base for testing PWP.
|
||||
|
||||
msg('Hello, World!').
|
||||
|
||||
status(tom, full_time).
|
||||
status(dick, part_time).
|
||||
status(harry, full_time).
|
||||
|
||||
staff(tom, 'Tom Cat', 1-21, 'x1234', 'tom@jerry.example.org').
|
||||
staff(dick, 'Dick Tater', 2-50, 'x9999', 'boss@hq.example.org').
|
||||
staff(harry, 'Harry Ett', 3-14, 'x7654', 'h.ett@kit.example.org').
|
||||
|
52
packages/http/examples/stress_client.pl
Normal file
52
packages/http/examples/stress_client.pl
Normal file
@@ -0,0 +1,52 @@
|
||||
/* $Id$
|
||||
|
||||
Part of SWI-Prolog
|
||||
|
||||
Author: Jan Wielemaker
|
||||
E-mail: wielemak@science.uva.nl
|
||||
WWW: http://www.swi-prolog.org
|
||||
Copyright (C): 1985-2007, University of Amsterdam
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
As a special exception, if you link this library with other files,
|
||||
compiled with a Free Software compiler, to produce an executable, this
|
||||
library does not by itself cause the resulting executable to be covered
|
||||
by the GNU General Public License. This exception does not however
|
||||
invalidate any other reasons why the executable file might be covered by
|
||||
the GNU General Public License.
|
||||
*/
|
||||
|
||||
:- module(http_stress_client,
|
||||
[ client/2, % +Port, +Test
|
||||
client/3 % +Port, +Test, +Times
|
||||
]).
|
||||
|
||||
:- use_module(library(http/http_client)).
|
||||
|
||||
/** <module> Sample HTTP client to run some stress tests
|
||||
|
||||
*/
|
||||
|
||||
client(Port, Action, Times) :-
|
||||
forall(between(1, Times, _),
|
||||
client(Port, Action)).
|
||||
|
||||
|
||||
client(Port, ping) :-
|
||||
http_get([ host(localhost),
|
||||
port(Port),
|
||||
path('/ping')
|
||||
], _, []).
|
129
packages/http/examples/stress_server.pl
Normal file
129
packages/http/examples/stress_server.pl
Normal file
@@ -0,0 +1,129 @@
|
||||
/* $Id$
|
||||
|
||||
Part of SWI-Prolog
|
||||
|
||||
Author: Jan Wielemaker
|
||||
E-mail: wielemak@science.uva.nl
|
||||
WWW: http://www.swi-prolog.org
|
||||
Copyright (C): 1985-2007, University of Amsterdam
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
As a special exception, if you link this library with other files,
|
||||
compiled with a Free Software compiler, to produce an executable, this
|
||||
library does not by itself cause the resulting executable to be covered
|
||||
by the GNU General Public License. This exception does not however
|
||||
invalidate any other reasons why the executable file might be covered by
|
||||
the GNU General Public License.
|
||||
*/
|
||||
|
||||
:- module(http_stress_server,
|
||||
[ server/1, % +Port
|
||||
profile/0
|
||||
]).
|
||||
:- load_files([ library(http/thread_httpd),
|
||||
library(http/html_write),
|
||||
library(http/http_session),
|
||||
library(http/http_dispatch),
|
||||
library(http/http_parameters),
|
||||
library(http/http_error),
|
||||
library(thread_pool)
|
||||
],
|
||||
[ silent(true)
|
||||
]).
|
||||
|
||||
/** <module> Sample HTTP server to run some stress tests
|
||||
|
||||
*/
|
||||
|
||||
%% server(+Port) is det.
|
||||
%
|
||||
% Start the server at Port.
|
||||
|
||||
server(Port) :-
|
||||
create_pools,
|
||||
server(Port,
|
||||
[ workers(1)
|
||||
]).
|
||||
|
||||
server(Port, Options) :-
|
||||
http_server(http_dispatch,
|
||||
[ port(Port),
|
||||
timeout(20)
|
||||
| Options
|
||||
]).
|
||||
|
||||
%% create_pools
|
||||
%
|
||||
% Create our thread pools.
|
||||
|
||||
create_pools :-
|
||||
thread_pool_create(single, 1, [backlog(0)]).
|
||||
|
||||
%% profile
|
||||
%
|
||||
% Run thread profiler on the one and only server.
|
||||
|
||||
profile :-
|
||||
findall(Id, http_current_worker(_, Id), Ids),
|
||||
( Ids = [Id]
|
||||
-> tprofile(Id)
|
||||
; Ids == []
|
||||
-> format(user_error, 'No HTTP server!~n', []),
|
||||
fail
|
||||
; format(user_error, 'Multiple HTPP workers: ~p~n', [Ids]),
|
||||
fail
|
||||
).
|
||||
|
||||
|
||||
/*******************************
|
||||
* METHODS *
|
||||
*******************************/
|
||||
|
||||
:- http_handler('/ping', ping, []).
|
||||
:- http_handler('/wait', wait, [chunked]).
|
||||
:- http_handler(prefix('/spawn/'), spawn, [spawn(single)]).
|
||||
:- http_handler(prefix('/spawn2/'), spawn, [spawn(single)]).
|
||||
|
||||
ping(_Request) :-
|
||||
format('Content-type: text/plain~n~n'),
|
||||
format('alife~n').
|
||||
|
||||
wait(Request) :-
|
||||
http_parameters(Request,
|
||||
[ wait(Time, [default(1)]),
|
||||
count(N, [default(10)])
|
||||
]),
|
||||
wait(Time, N).
|
||||
|
||||
wait(Time, N) :-
|
||||
format('Content-type: text/plain~n~n'),
|
||||
forall(between(1, N, I),
|
||||
( sleep(Time),
|
||||
format('~D~n', [I]),
|
||||
flush_output
|
||||
)).
|
||||
|
||||
%% spawn(+Request)
|
||||
%
|
||||
% Run requests under /spawn/ in their own thread.
|
||||
|
||||
spawn(Request) :-
|
||||
selectchk(path(Path), Request, Request1),
|
||||
( sub_atom(Path, Start, _, _, /), Start > 0
|
||||
-> sub_atom(Path, Start, _, 0, NewPath)
|
||||
),
|
||||
http_dispatch([path(NewPath)|Request1]).
|
||||
|
Reference in New Issue
Block a user