diff options
author | Maxime Devos <maximedevos@telenet.be> | 2021-08-12 16:04:44 +0200 |
---|---|---|
committer | Maxime Devos <maximedevos@telenet.be> | 2021-09-21 12:13:35 +0200 |
commit | cb3cd67b31a03d2acb40c7d64528865a69f46ab8 (patch) | |
tree | 148c7963b0f7f5cbb348c6b48387f7451966a68e /tests | |
parent | df373e2ad6d2fd5bdde80921082c8c4ea1d69094 (diff) | |
download | gnunet-scheme-cb3cd67b31a03d2acb40c7d64528865a69f46ab8.tar.gz gnunet-scheme-cb3cd67b31a03d2acb40c7d64528865a69f46ab8.zip |
mq-impl/stream: Implement connecting to unix sockets.
* README.org
(Message queues): Note existence of 'connect-unix'.
(List of errors): Mention 'connection:connected'.
* gnu/gnunet/mq-impl/stream.scm
(%path-resolution-errors): New variable.
(connect-unix): New procedure.
(connect/fibers): New procedure.
* tests/mq-stream.scm
(call-with-temporary-directory)
(make-config)
(call-with-socket-location)
(connect/test)
(alist->hash-table)
(test-connection)
(yield-many): New procedures.
("connect-unix, can connect when socket is already listening")
("connect-unix, will connect when socket is listening")
("connect-unix, will connect when socket is bound (and listening)")
("connect-unix, will connect even if there's an old socket lying
around")
("connect-unix, will connect even if previous socket is different
type")
("connect-unix, will connect even if permissions are temporarily
wrong"): New tests.
Diffstat (limited to 'tests')
-rw-r--r-- | tests/mq-stream.scm | 173 |
1 files changed, 172 insertions, 1 deletions
diff --git a/tests/mq-stream.scm b/tests/mq-stream.scm index fa70a1d..1fce3f5 100644 --- a/tests/mq-stream.scm +++ b/tests/mq-stream.scm | |||
@@ -21,6 +21,7 @@ | |||
21 | (gnu gnunet mq handler) | 21 | (gnu gnunet mq handler) |
22 | (gnu gnunet utils hat-let) | 22 | (gnu gnunet utils hat-let) |
23 | (gnu gnunet utils bv-slice) | 23 | (gnu gnunet utils bv-slice) |
24 | (gnu gnunet config db) | ||
24 | (gnu gnunet concurrency repeated-condition) | 25 | (gnu gnunet concurrency repeated-condition) |
25 | (fibers conditions) | 26 | (fibers conditions) |
26 | (fibers operations) | 27 | (fibers operations) |
@@ -28,12 +29,15 @@ | |||
28 | (rnrs bytevectors) | 29 | (rnrs bytevectors) |
29 | ((rnrs io ports) #:select (open-bytevector-input-port)) | 30 | ((rnrs io ports) #:select (open-bytevector-input-port)) |
30 | ((rnrs base) #:select (assert)) | 31 | ((rnrs base) #:select (assert)) |
32 | (rnrs hashtables) | ||
31 | (srfi srfi-26) | 33 | (srfi srfi-26) |
32 | (srfi srfi-43) | 34 | (srfi srfi-43) |
33 | (rnrs io ports) | 35 | (rnrs io ports) |
34 | (ice-9 binary-ports) | 36 | (ice-9 binary-ports) |
35 | (ice-9 suspendable-ports) | 37 | (ice-9 suspendable-ports) |
36 | (ice-9 control)) | 38 | (ice-9 control) |
39 | (ice-9 match) | ||
40 | (ice-9 threads)) | ||
37 | 41 | ||
38 | (define (no-sender . _) | 42 | (define (no-sender . _) |
39 | (error "no sender!")) | 43 | (error "no sender!")) |
@@ -231,4 +235,171 @@ | |||
231 | #:parallelism 1 | 235 | #:parallelism 1 |
232 | #:hz 0))) | 236 | #:hz 0))) |
233 | 237 | ||
238 | (define (call-with-temporary-directory proc) | ||
239 | (let ((file (mkdtemp (in-vicinity (or (getenv "TMPDIR") "/tmp") | ||
240 | "test-XXXXXX")))) | ||
241 | (with-exception-handler | ||
242 | (lambda (e) | ||
243 | (system* "rm" "-r" file) | ||
244 | (raise-exception e)) | ||
245 | (lambda () | ||
246 | (call-with-values | ||
247 | (lambda () (proc file)) | ||
248 | (lambda the-values | ||
249 | (system* "rm" "-r" file) | ||
250 | (apply values the-values))))))) | ||
251 | |||
252 | (define (make-config where) | ||
253 | (hash->configuration | ||
254 | (alist->hash-table | ||
255 | `((("service" . "UNIXPATH") . ,where))))) | ||
256 | |||
257 | (define (call-with-socket-location proc) | ||
258 | (call-with-temporary-directory | ||
259 | (lambda (dir) | ||
260 | (define where (in-vicinity dir "sock.et")) | ||
261 | (define config (make-config where)) | ||
262 | (proc where config)))) | ||
263 | |||
264 | (define (connect/test config connected?) | ||
265 | (define (error-handler . error) | ||
266 | (match error | ||
267 | ;; XXX correct documentation: multiple end-of-files | ||
268 | ;; are perfectly possible. | ||
269 | (('input:regular-end-of-file) (values)) | ||
270 | (('connection:connected) (signal-condition! connected?)))) | ||
271 | (connect/fibers config "service" no-handlers error-handler | ||
272 | #:spawn call-with-new-thread)) | ||
273 | |||
274 | (define (alist->hash-table alist) | ||
275 | (define h (make-hashtable (lambda (key) 0) equal?)) | ||
276 | (define (insert! key+value) | ||
277 | (hashtable-set! h (car key+value) (cdr key+value))) | ||
278 | (for-each insert! alist) | ||
279 | h) | ||
280 | |||
281 | (define (test-connection mq server-sock) | ||
282 | (send-message! mq (bv-slice/read-write #vu8(0 4 0 0))) | ||
283 | (let ((client (car (accept server-sock)))) | ||
284 | (assert (equal? #vu8(0 4 0 0) (get-bytevector-n client 4))) | ||
285 | #t)) | ||
286 | |||
287 | (define (yield-many) | ||
288 | ;; Give the new threads some time to run before binding the socket. | ||
289 | ;; This allowed a bug in the use of 'connect' to be detected. | ||
290 | (let loop ((n (* 8 (+ 1 (length (all-threads)))))) | ||
291 | (when (> n 0) | ||
292 | (yield) | ||
293 | (loop (- n 1))))) | ||
294 | |||
295 | (test-assert "connect-unix, can connect when socket is already listening" | ||
296 | (call-with-socket-location | ||
297 | (lambda (where config) | ||
298 | (define listening-sock (socket PF_UNIX SOCK_STREAM 0)) | ||
299 | (define connected? (make-condition)) | ||
300 | (bind listening-sock AF_UNIX where) | ||
301 | (listen listening-sock 1) | ||
302 | (define mq (connect/test config connected?)) | ||
303 | (wait connected?) | ||
304 | (test-connection mq listening-sock)))) | ||
305 | |||
306 | ;; Consider the case where a service starts, has bound its socket | ||
307 | ;; but is not yet listening, and a client connects. | ||
308 | (test-assert "connect-unix, will connect when socket is listening" | ||
309 | (call-with-socket-location | ||
310 | (lambda (where config) | ||
311 | (define listening-sock (socket PF_UNIX SOCK_STREAM 0)) | ||
312 | (define connected? (make-condition)) | ||
313 | (bind listening-sock AF_UNIX where) | ||
314 | (define mq (connect/test config connected?)) | ||
315 | (yield-many) | ||
316 | (listen listening-sock 1) | ||
317 | (wait connected?) | ||
318 | (test-connection mq listening-sock)))) | ||
319 | |||
320 | ;; Consider the case where a client starts before a service. | ||
321 | (test-assert "connect-unix, will connect when socket is bound (and listening)" | ||
322 | (call-with-socket-location | ||
323 | (lambda (where config) | ||
324 | (define listening-sock (socket PF_UNIX SOCK_STREAM 0)) | ||
325 | (define connected? (make-condition)) | ||
326 | (define mq (connect/test config connected?)) | ||
327 | (yield-many) | ||
328 | (bind listening-sock AF_UNIX where) | ||
329 | (listen listening-sock 1) | ||
330 | (wait connected?) | ||
331 | (test-connection mq listening-sock)))) | ||
332 | |||
333 | ;; Consider the case where a service starts and stops, | ||
334 | ;; a client connects and the service restarts. | ||
335 | (test-assert | ||
336 | "connect-unix, will connect even if there's an old socket lying around" | ||
337 | (call-with-socket-location | ||
338 | (lambda (where config) | ||
339 | (let ((old-sock (socket PF_UNIX SOCK_STREAM 0))) | ||
340 | (bind old-sock AF_UNIX where) | ||
341 | (close-port old-sock)) | ||
342 | (define connected? (make-condition)) | ||
343 | (define mq (connect/test config connected?)) | ||
344 | (yield-many) | ||
345 | (define listening-sock (socket PF_UNIX SOCK_STREAM 0)) | ||
346 | (yield-many) | ||
347 | ;; Delete the old socket, otherwise the 'bind' below results in ‘address alreay in use’ | ||
348 | (delete-file where) | ||
349 | (yield-many) | ||
350 | (bind listening-sock AF_UNIX where) | ||
351 | (yield-many) | ||
352 | (listen listening-sock 1) | ||
353 | (wait connected?) | ||
354 | (test-connection mq listening-sock)))) | ||
355 | |||
356 | ;; Consider the case where GNUnet version N uses stream sockets, | ||
357 | ;; GNUnet version M uses datagram sockets, the system initially | ||
358 | ;; uses GNUnet version N, a client for version M is started | ||
359 | ;; (initially failing to connect to the server), then the system | ||
360 | ;; switches to GNUnet version M. | ||
361 | (test-assert | ||
362 | "connect-unix, will connect even if previous socket is different type" | ||
363 | (call-with-socket-location | ||
364 | (lambda (where config) | ||
365 | (define old-sock (socket PF_UNIX SOCK_DGRAM 0)) | ||
366 | (bind old-sock AF_UNIX where) | ||
367 | ;; Datagram sockets don't support 'listen', so don't | ||
368 | ;; call 'listen' with 'old-sock'. | ||
369 | (define connected? (make-condition)) | ||
370 | (define mq (connect/test config connected?)) | ||
371 | (yield-many) | ||
372 | (close-port old-sock) | ||
373 | (delete-file where) | ||
374 | (define new-sock (socket PF_UNIX SOCK_STREAM 0)) | ||
375 | (bind new-sock AF_UNIX where) | ||
376 | (listen new-sock 1) | ||
377 | (wait connected?) | ||
378 | (test-connection mq new-sock)))) | ||
379 | |||
380 | ;; Consider a system that creates directories and the socket | ||
381 | ;; with world-unreadable, world-unexecutable permissions at | ||
382 | ;; first and makes the permissions more permissive later. | ||
383 | (test-assert | ||
384 | "connect-unix, will connect even if permissions are temporarily wrong" | ||
385 | (call-with-temporary-directory | ||
386 | (lambda (tmpdir) | ||
387 | ;; Permissions on sockets can be unreliable on some systems, | ||
388 | ;; so modify the permissions of a directory instead. | ||
389 | (define subdir (in-vicinity tmpdir "dir")) | ||
390 | (mkdir subdir) | ||
391 | (define where (in-vicinity subdir "sock.et")) | ||
392 | (define listening-sock (socket PF_UNIX SOCK_STREAM 0)) | ||
393 | (bind listening-sock AF_UNIX where) | ||
394 | (listen listening-sock 1) | ||
395 | (chmod subdir #o000) ; unreadable | ||
396 | (define connected? (make-condition)) | ||
397 | (define mq (connect/test (make-config where) connected?)) | ||
398 | (yield-many) | ||
399 | ;; make it readable again | ||
400 | ;; (and writable such that 'tmpdir' can be deleted). | ||
401 | (chmod subdir #o700) | ||
402 | (wait connected?) | ||
403 | (test-connection mq listening-sock)))) | ||
404 | |||
234 | (test-end "mq-stream") | 405 | (test-end "mq-stream") |