chickadee » schematra » close-websocket!

close-websocket! #!optional code reason wsprocedure

Initiate a graceful close.

  • code: integer close code (default 1000 for normal closure).
  • reason: human-readable string (default "").
  • ws: the connection to close (default (current-websocket)).

Sends the close frame to the peer (best effort) and tears down the underlying input port so the connection's read loop unblocks immediately — even when called from another thread. The connection's on-close clause still runs for cleanup.

;; From inside a handler — close the current connection (code 1000):
(on-text message
  (when (string=? message "/bye")
    (close-websocket!)))

;; From another thread — force-close a specific stored connection:
(close-websocket! 1008 "policy violation" some-other-ws)

Broadcasting: because current-websocket is a first-class value, you can keep a registry of connections and write to each by parameterizing the connection. Always snapshot the registry under a lock before iterating, then release the lock — send-text can block on slow clients, and you don't want to hold the lock during I/O.

(define clients-mutex (make-mutex))
(define clients '())

(define (broadcast! message)
  (let ((snapshot
         (dynamic-wind
           (lambda () (mutex-lock! clients-mutex))
           (lambda () clients)
           (lambda () (mutex-unlock! clients-mutex)))))
    (for-each
     (lambda (ws)
       (parameterize ((current-websocket ws))
         (condition-case (send-text message)
           (exn () (set! clients (delete ws clients))))))
     snapshot)))

(websocket "/chat"
  (on-open  (set! clients (cons (current-websocket) clients))
            (broadcast! "a new client joined"))
  (on-text message (broadcast! message))
  (on-close code reason
            (set! clients (delete (current-websocket) clients))
            (broadcast! "a client left")))

Error handling: if a handler raises an exception, Schematra runs your on-error clause first, then sends a 1011 close frame, runs on-close, and lets the framework log the original exception. The connection is always closed cleanly, so you don't need to wrap send-text in condition-case for normal usage.

Configuration: three parameters bound incoming traffic, all in schematra.ws: