-
Notifications
You must be signed in to change notification settings - Fork 25
/
test_websocket.pl
176 lines (148 loc) · 5.56 KB
/
test_websocket.pl
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
:- 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").