1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
|
:- module(test_websocket,
[ test_websocket/0
]).
:- asserta(user:file_search_path(foreign, '.')).
:- asserta(user:file_search_path(foreign, '../clib')).
:- asserta(user:file_search_path(foreign, '../sgml')).
:- asserta(user:file_search_path(library, '..')).
:- asserta(user:file_search_path(library, '../sgml')).
:- asserta(user:file_search_path(library, '../plunit')).
:- asserta(user:file_search_path(library, '../clib')).
:- use_module(library(plunit)).
:- use_module(library(http/websocket)).
:- use_module(library(apply)).
:- use_module(library(option)).
:- use_module(library(debug)).
:- use_module(library(lists)).
:- use_module(library(http/thread_httpd)).
:- use_module(library(http/http_dispatch)).
test_websocket :-
run_tests([ serialization,
http
]).
:- begin_tests(serialization).
test(text, Reply == [ websocket{opcode:text, format:string, data:"Hello world"}
]) :-
ws_loop_close([text("Hello world")], Reply, []).
test(unicode, Reply == [ websocket{opcode:text, format:string, data:Data}
]) :-
unicode_data(Data),
ws_loop_close([text(Data)], Reply, []).
test(prolog, Reply == [ websocket{opcode:text, format:prolog, data:hello(world)}
]) :-
ws_loop_close([prolog(hello(world))], Reply, [format(prolog)]).
test(json, Reply =@= [ websocket{opcode:text, format:json, data:_{hello:world}}
]) :-
ws_loop_close([json(_{hello:world})], Reply,
[ format(json),
value_string_as(atom)
]).
test(split, Reply == [ websocket{opcode:text, format:string, data:"0123456789"}
]) :-
ws_loop_close([text("0123456789"), close], Reply,
[ buffer_size(5)
]).
:- end_tests(serialization).
:- begin_tests(http).
test(echo, Reply == [ websocket{opcode:text, format:string, data:"Hello world"},
websocket{opcode:text, format:string, data:Unicode},
websocket{opcode:close, code:1005, format:string, data:"Ciao"}
]) :-
Address = localhost:Port,
unicode_data(Unicode),
setup_call_cleanup(
server(Address),
client(Port,
[ text("Hello world"),
text(Unicode),
close(1005, "Ciao")
],
Reply),
http_stop_server(Port, [])).
:- end_tests(http).
unicode_data(
"\u0420\u0443\u0441\u0441\u043A\u0438\u0439 \u044F\u0437\u044B\u043A").
/*******************************
* SERIALIZATION SUPPORT *
*******************************/
ws_loop_close(Messages, Result, Options) :-
append(Messages, [close], Messages1),
ws_loop(Messages1, Result0, Options),
once(append(Result, [Close], Result0)),
assertion(Close == websocket{opcode:close,
format:string,
code:1000,
data:""}).
ws_loop(Messages, Result, Options) :-
is_list(Messages),
!,
setup_call_cleanup(
tmp_file(ws, File),
( ws_write_file(File, Messages, Options),
ws_read_file(File, Result, Options)
),
delete_file(File)).
ws_loop(Message, Result, Options) :-
ws_loop([Message], Result, Options).
ws_write_file(File, Messages, Options) :-
option(close_parent(true), Options, true),
!,
open(File, write, Out, [type(binary)]),
ws_write_stream(Out, Messages, Options).
ws_write_file(File, Messages, Options) :-
setup_call_cleanup(
open(File, write, Out, [type(binary)]),
ws_write_stream(Out, Messages, Options),
close(Out)).
ws_write_stream(Stream, Messages, Options) :-
setup_call_cleanup(
ws_open(Stream, WsStream, Options),
maplist(ws_send(WsStream), Messages),
close(WsStream)).
/*******************************
* READ *
*******************************/
ws_read_file(File, Message, Options) :-
setup_call_cleanup(
open(File, read, In, [type(binary)]),
ws_read_stream(In, Message, Options),
close(In)).
ws_read_stream(Stream, Messages, Options) :-
setup_call_cleanup(
ws_open(Stream, WsStream, [close_parent(false)]),
ws_receive_all(WsStream, Messages, Options),
close(WsStream)).
ws_receive_all(WsStream, Messages, Options) :-
ws_receive(WsStream, H, Options),
( H == end_of_file
-> Messages = []
; Messages = [H|T],
( H.opcode == close
-> T = []
; ws_receive_all(WsStream, T, Options)
)
).
/*******************************
* HTTP *
*******************************/
:- http_handler(root(echo),
http_upgrade_to_websocket(echo,
[ subprotocols([echo])
]),
[spawn([])]).
server(Port) :-
http_server(http_dispatch, [port(Port)]).
echo(WebSocket) :-
ws_receive(WebSocket, Message),
debug(websocket, 'Got ~p', [Message]),
ws_send(WebSocket, Message),
( Message.opcode == close
-> true
; echo(WebSocket)
).
client(Port, Messages, Reply) :-
format(string(URL), 'ws://localhost:~d/echo', [Port]),
http_open_websocket(URL, WebSocket, []),
maplist(ws_send(WebSocket), Messages),
ws_receive_all(WebSocket, Reply, []),
ws_close(WebSocket, 1000, "bye").
|