35
36:- module(http_ssl_plugin, []). 38:- use_module(library(ssl),
39 [ ssl_context/3,
40 ssl_secure_ciphers/1,
41 ssl_property/2,
42 ssl_set_options/3,
43 ssl_negotiate/5
44 ]). 45:- use_module(library(debug),[debug/3]). 46:- use_module(library(socket),
47 [ tcp_socket/1,
48 tcp_setopt/2,
49 tcp_bind/2,
50 tcp_listen/2,
51 tcp_accept/3,
52 tcp_open_socket/3,
53 tcp_connect/3
54 ]). 55
56:- autoload(library(lists),[select/3]). 57:- autoload(library(option),[option/2,option/3]). 58:- autoload(library(apply), [include/3]). 59:- autoload(library(http/http_header),[http_read_reply_header/2]). 60:- autoload(library(http/thread_httpd),[http_enough_workers/3]).
73:- multifile
74 thread_httpd:make_socket_hook/3,
75 thread_httpd:accept_hook/2,
76 thread_httpd:open_client_hook/6,
77 thread_httpd:discard_client_hook/1,
78 http:http_protocol_hook/5,
79 http:open_options/2,
80 http:http_connection_over_proxy/6,
81 http:ssl_server_create_hook/3,
82 http:ssl_server_open_client_hook/3. 83
84
85
97thread_httpd:make_socket_hook(Port, M:Options0, Options) :-
98 select(ssl(SSLOptions0), Options0, Options1),
99 !,
100 add_secure_ciphers(SSLOptions0, SSLOptions1),
101 disable_sslv3(SSLOptions1, SSLOptions),
102 make_socket(Port, Socket, Options1),
103 ssl_context(server, SSL0, M:[close_parent(true)|SSLOptions]),
104 ( http:ssl_server_create_hook(SSL0, SSL1, Options1)
105 -> ensure_close_parent(SSL1, SSL)
106 ; SSL = SSL0
107 ),
108 port(Port, PortNum),
109 atom_concat('httpsd', PortNum, Queue),
110 Options = [ queue(Queue),
111 tcp_socket(Socket),
112 ssl_instance(SSL)
113 | Options1
114 ].
115
116port(_Host:Port0, Port) => Port = Port0.
117port(Port0, Port), integer(Port0) => Port = Port0.
118
119ensure_close_parent(SSL0, SSL) :-
120 ( ssl_property(SSL0, close_parent(true))
121 -> SSL = SSL0
122 ; ssl_set_options(SSL0, SSL, [close_parent(true)])
123 ).
129add_secure_ciphers(SSLOptions0, SSLOptions) :-
130 ( option(cipher_list(_), SSLOptions0)
131 -> SSLOptions = SSLOptions0
132 ; ssl_secure_ciphers(Ciphers),
133 SSLOptions = [cipher_list(Ciphers)|SSLOptions0]
134 ).
142disable_sslv3(SSLOptions0, SSLOptions) :-
143 ( option(min_protocol_version(_), SSLOptions0)
144 ; option(disable_ssl_methods(_), SSLOptions0)
145 ),
146 !,
147 SSLOptions = SSLOptions0.
148disable_sslv3(SSLOptions0,
149 [ disable_ssl_methods([sslv3,sslv23]), 150 min_protocol_version(tlsv1) 151 | SSLOptions0
152 ]).
153
154
155make_socket(_Port, Socket, Options) :-
156 option(tcp_socket(Socket), Options),
157 !.
158make_socket(Port, Socket, _Options) :-
159 tcp_socket(Socket),
160 tcp_setopt(Socket, reuseaddr),
161 tcp_bind(Socket, Port),
162 tcp_listen(Socket, 5).
169thread_httpd:accept_hook(Goal, Options) :-
170 memberchk(ssl_instance(SSL0), Options),
171 !,
172 ensure_close_parent(SSL0, SSL),
173 memberchk(queue(Queue), Options),
174 memberchk(tcp_socket(Socket), Options),
175 tcp_accept(Socket, Client, Peer),
176 sig_atomic(send_to_worker(Queue, SSL, Client, Goal, Peer)),
177 http_enough_workers(Queue, accept, Peer).
178
179send_to_worker(Queue, SSL, Client, Goal, Peer) :-
180 debug(http(connection), 'New HTTPS connection from ~p', [Peer]),
181 thread_send_message(Queue, ssl_client(SSL, Client, Goal, Peer)).
187thread_httpd:discard_client_hook(ssl_client(_SSL, Client, _Goal, _Peer)) :-
188 tcp_close_socket(Client).
212thread_httpd:open_client_hook(ssl_client(SSL0, Client, Goal, Peer),
213 Goal, In, Out,
214 [peer(Peer), protocol(https)],
215 Options) :-
216 ( http:ssl_server_open_client_hook(SSL0, SSL, Options)
217 -> true
218 ; SSL = SSL0
219 ),
220 option(timeout(TMO), Options, 60),
221 tcp_open_socket(Client, Read, Write),
222 set_stream(Read, timeout(TMO)),
223 set_stream(Write, timeout(TMO)),
224 catch(ssl_negotiate(SSL, Read, Write, In, Out),
225 E,
226 ssl_failed(Read, Write, E)).
227
228ssl_failed(Read, Write, E) :-
229 close(Write, [force(true)]),
230 close(Read, [force(true)]),
231 throw(E).
232
233
234
244http:http_protocol_hook(https, Parts, PlainStreamPair, StreamPair, Options) :-
245 ssl_protocol_hook(Parts, PlainStreamPair, StreamPair, Options).
246http:http_protocol_hook(wss, Parts, PlainStreamPair, StreamPair, Options) :-
247 ssl_protocol_hook(Parts, PlainStreamPair, StreamPair, Options).
248
249ssl_protocol_hook(Parts, PlainStreamPair, StreamPair, Options) :-
250 memberchk(host(Host), Parts),
251 include(ssl_option, Options, SSLOptions),
252 ssl_context(client, SSL, [ host(Host),
253 close_parent(true)
254 | SSLOptions
255 ]),
256 stream_pair(PlainStreamPair, PlainIn, PlainOut),
257 258 ssl_negotiate(SSL, PlainIn, PlainOut, In, Out),
259 stream_pair(StreamPair, In, Out).
260
263
264ssl_option(Term) :-
265 compound(Term),
266 compound_name_arity(Term, _, 1).
275http:http_connection_over_proxy(proxy(ProxyHost, ProxyPort), Parts,
276 Host:Port, StreamPair, Options, Options) :-
277 memberchk(scheme(https), Parts),
278 !,
279 tcp_connect(ProxyHost:ProxyPort, StreamPair, [bypass_proxy(true)]),
280 catch(negotiate_http_connect(StreamPair, Host:Port),
281 Error,
282 ( close(StreamPair, [force(true)]),
283 throw(Error)
284 )).
285
286negotiate_http_connect(StreamPair, Address):-
287 format(StreamPair, 'CONNECT ~w HTTP/1.1\r\n\r\n', [Address]),
288 flush_output(StreamPair),
289 http_read_reply_header(StreamPair, Header),
290 memberchk(status(_, Status, Message), Header),
291 ( Status == ok
292 -> true
293 ; throw(error(proxy_rejection(Message), _))
294 )
SSL plugin for HTTP libraries
This module can be loaded next to
library(thread_httpd)
andlibrary(http_open)
to provide secure HTTP (HTTPS) services and client access.An example secure server using self-signed certificates can be found in the <plbase>/
doc/packages/examples/ssl/https.pl
, where <plbase> is the SWI-Prolog installation directory. */