;;; SPDX-License-Identifier: LGPL-3.0-or-later ;;; SPDX-FileCopyrightText: Copyright © 2010-2021 Tony Garnock-Jones #lang syndicate (provide generic-client-session-facet) (require "wire-protocol.rkt") (require "internal-protocol.rkt") (require "protocol.rkt") (require "turn.rkt") (require syndicate/term) (require/activate "heartbeat.rkt") (define-logger syndicate/distributed) (spawn #:name 'client-factory (during (to-server $a _) (assert (server-connection a))) (during (observe (from-server $a _)) (assert (server-connection a))) (during (observe (server-connected $a)) (assert (server-connection a)))) (struct sub (spec [captures #:mutable]) #:transparent) (define (generic-client-session-facet address scope w) (on-start (log-syndicate/distributed-info "Connected to ~v" address)) (on-stop (log-syndicate/distributed-info "Disconnected from ~v" address)) (assert (server-connected address)) (assert (server-session-connected address)) (when (log-level? syndicate/distributed-logger 'debug) (set! w (let ((w* w)) (lambda (p) (log-syndicate/distributed-debug "C OUT ~v ~v" address p) (w* p))))) (define turn (turn-recorder (lambda (items) (w (Turn items))))) (define next-ep (let ((counter 0)) (lambda () (begin0 counter (set! counter (+ counter 1)))))) (define pubs (hash)) (define subs (hash)) (define matches (hash)) (on-start (w (Connect scope))) (on-stop (for* [(s (in-hash-values matches)) (a (in-hash-values (sub-captures s)))] (retract! a))) (define (instantiate s vs) (instantiate-term->value (from-server address (sub-spec s)) vs)) (on (asserted (to-server address $a)) (define ep (next-ep)) (extend-turn! turn (Assert ep a)) (set! pubs (hash-set pubs a ep))) (on (retracted (to-server address $a)) (define ep (hash-ref pubs a)) (extend-turn! turn (Clear ep)) (set! pubs (hash-remove pubs a))) (on (message (to-server address $a)) (extend-turn! turn (Message a))) (on (asserted (observe (from-server address $spec))) (define ep (next-ep)) (extend-turn! turn (Assert ep (observe spec))) (set! subs (hash-set subs spec ep)) (set! matches (hash-set matches ep (sub spec (hash))))) (on (retracted (observe (from-server address $spec))) (extend-turn! turn (Clear (hash-ref subs spec))) (set! subs (hash-remove subs spec))) (define reset-heartbeat! (heartbeat (list 'client address scope) w (lambda () (stop-current-facet)))) (on (message (server-packet address _)) (reset-heartbeat!)) (on (message (server-packet address (Ping))) (w (Pong))) (on (message (server-packet address (Err $detail $context))) (log-syndicate/distributed-error "Error from ~a: ~v~a" address detail (if context (format " ~v" context) "")) (stop-current-facet)) (on (message (server-packet address (Turn $items))) (for [(item (in-list items))] (match item [(Add ep vs) (let* ((s (hash-ref matches ep)) (a (instantiate s vs))) (set-sub-captures! s (hash-set (sub-captures s) vs a)) (assert! a))] [(Del ep vs) (let* ((s (hash-ref matches ep)) (a (hash-ref (sub-captures s) vs))) (retract! a) (set-sub-captures! s (hash-remove (sub-captures s) vs)))] [(Msg ep vs) (let* ((s (hash-ref matches ep))) (send! (instantiate s vs)))] [(End ep) (let* ((s (hash-ref matches ep #f))) (when s (for [(a (in-hash-values (sub-captures s)))] (retract! a)) (set! matches (hash-remove matches ep))))]))))