{"id":33237023,"url":"https://github.com/3b/clws","last_synced_at":"2026-01-23T02:35:41.024Z","repository":{"id":966977,"uuid":"759655","full_name":"3b/clws","owner":"3b","description":"websockets server in CL","archived":false,"fork":false,"pushed_at":"2025-10-31T23:09:12.000Z","size":143,"stargazers_count":75,"open_issues_count":7,"forks_count":6,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-11-01T01:08:55.794Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Common Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/3b.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2010-07-06T13:52:12.000Z","updated_at":"2025-10-31T23:09:16.000Z","dependencies_parsed_at":"2022-08-12T12:41:06.354Z","dependency_job_id":null,"html_url":"https://github.com/3b/clws","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/3b/clws","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/3b%2Fclws","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/3b%2Fclws/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/3b%2Fclws/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/3b%2Fclws/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/3b","download_url":"https://codeload.github.com/3b/clws/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/3b%2Fclws/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28679040,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-23T01:00:35.747Z","status":"online","status_checked_at":"2026-01-23T02:00:08.296Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-11-16T19:00:28.311Z","updated_at":"2026-01-23T02:35:41.015Z","avatar_url":"https://github.com/3b.png","language":"Common Lisp","readme":"# CLWS - a [WebSocket][] server in Common Lisp\n\nCurrently requires SBCL or CCL, but shouldn't be too hard to port to\nother implementations/platforms supported by iolib.\n\nSupports [WebSocket][] draft protocols [7][],[8][], and [13][], and optionally\n[0][].\n\nDoesn't currently support `wss:` (TLS/SSL) connections, but proxying behind [stud][] or [stunnel][] should work.\n\n[WebSocket]: http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-15\n[hixie]: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol\n[0]: http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00\n[7]: http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-07\n[8]: http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-08\n[13]: http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-15\n[stud]: https://github.com/bumptech/stud\n[stunnel]: http:www.stunnel.org/\n\n\n\n## Sample usage: Echo Server\n\nFirst, set up a package:\n\n```lisp\n(defpackage #:clws-echo\n  (:use :cl :clws))\n\n(in-package #:clws-echo)\n\n```\n\nThen we can start the websockets server, here we use port 12345:\n\n```lisp\n(bordeaux-threads:make-thread (lambda ()\n                                (run-server 12345))\n                              :name \"websockets server\")\n```\n\nNext we need to define a 'resource', which we will call `/echo` (so we will connect with URIs like `ws://localhost/echo`). To do that, we subclass `ws-resource` and specialize a few generic functions on that class:\n\n```lisp\n(defclass echo-resource (ws-resource)\n  ())\n\n(defmethod resource-client-connected ((res echo-resource) client)\n  (format t \"got connection on echo server from ~s : ~s~%\" (client-host client) (client-port client))\n  t)\n\n(defmethod resource-client-disconnected ((resource echo-resource) client)\n  (format t \"Client disconnected from resource ~A: ~A~%\" resource client))\n\n(defmethod resource-received-text ((res echo-resource) client message)\n  (format t \"got frame ~s from client ~s\" message client)\n  (write-to-client-text client message))\n\n(defmethod resource-received-binary((res echo-resource) client message)\n  (format t \"got binary frame ~s from client ~s\" (length message) client)\n  (write-to-client-binary client message))\n```\n\nFinally, we register the resource with the server, and start a thread to handle messages for that resource:\n\n```lisp\n(register-global-resource \"/echo\"\n                          (make-instance 'echo-resource)\n                          (origin-prefix \"http://127.0.0.1\" \"http://localhost\"))\n\n(bordeaux-threads:make-thread (lambda ()\n                                (run-resource-listener\n                                 (find-global-resource \"/echo\")))\n                              :name \"resource listener for /echo\")\n```\n\n\n## Configuration variables\n\n* `*protocol-76/00-support*`: set to T to enable support for [draft-hixie-76][hixie]/[draft-ietf-hybi-00][0] protocol  \n  No longer used by current browsers, and doesn't support binary frames. May go away soon.\n\n* `*default-socket-backlog*`: default socket backlog parameter for the server listener (default: 5).\n  Controls how many pending connections can be queued before the OS starts rejecting them. Can be overridden with the `:backlog` keyword argument to `run-server`.\n\n* `*max-clients*`: maximum number of simultaneous connections allowed, or `NIL` for no limit\n\n* `*max-read-frame-size*`, `*max-read-message-size*`: maximum 'frame' and 'message' sizes allowed from clients.  \n  Malicious clients can cause the server to buffer up to `*max-read-message-size*` per connection, so these should probably be reduced as much as possible for production servers.\n\n* `*debug-on-server-errors*`, `*debug-on-resource-errors*`: set to T to enter debugger on errors instead of dropping connections, for the server thread and resource handler thread respectively.\n\n## Resource handler API\n\n* `register-global-resource (name resource-handler origin-validation-function)`:  \n  Registers `resource-handler` for the resource `name`, which should be the `abs_path` part of a URI, like `/foo/bar`, `origin-validation-function` should be a function of one argument which returns true for any origin from which connections will be accepted. See `any-origin`,`origin-prefix`,`origin-exact`. (\"Origin\" in this case refers to the value of the `Origin` or `Sec-Webcosket-Origin` header required to be sent by browsers, specifying the host from which the page was loaded, like `http://localhost`.)\n\n* `resource-received-text (resource client message)`: Resource handlers should specialize this generic function to handle `text` messages from clients. `message` is a lisp `string` containing the message from `client`.\n\n* `resource-received-binary (resource client message)`: Resource handlers should specialize this generic function to handle `binary` messages from clients. `message` is a `(vector (unsigned-byte 8))` containing the message from `client`.\n\n* `resource-received-pong (resource client message)`: Resource handlers can specialize this generic function to handle `pong` frames from clients. `message` is the payload data from the pong frame (may be empty). This is typically used for latency testing and connection keep-alive monitoring when combined with `write-to-client-ping`.\n\n* `resource-client-connected (resource client)`: Called to notify a resource handler when a client connects. If the handler objects to a particular client for some reason, it can return `:reject` to close the connection and ignore any already received data from that client.\n\n* `resource-client-disconnected (resource client)`: Called when a client disconnects.\n\n## Sending data to clients\n\n* `write-to-client-text (client message \u0026key frame-size)`: Send `message` to `client`. `message` should be a CL `string` or a `(simple-array (unsigned-byte 8) (*))` containing a utf-8 encoded string. If `frame-size` is not `nil`, message will be broken up into frames of `frame-size` octets.\n\n* `write-to-clients-text (clients message \u0026key frame-size)`: Send `message` to a list of `clients`. Same as `write-to-client-text`, but tries to avoid repeated processing (utf-8 encoding, building frames, etc) that can be shared between clients.\n\n* `write-to-client-binary (client message \u0026key frame-size)`: Send `message` to `client`. `message` should be a `(simple-array (unsigned-byte 8) (*))`.  If `frame-size` is not `nil`, message will be broken up into frames of `frame-size` octets.\n\n* `write-to-clients-binary (clients message \u0026key frame-size)`: Send `message` to a list of `clients`. Same as `write-to-client-binary`, but tries to avoid repeated processing (utf-8 encoding, building frames, etc) that can be shared between clients.\n\n* `write-to-client-close (client \u0026key (code 1000) message)`: Send a `close` message to `client`. `code` specifies the 'status code' to be send in the close message (see the [websocket spec][http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-15#section-7.4] for valid codes) defaults to 1000, indicating \"normal closure\".  `message` can be a short string (must utf-8 encode to \u003c 123 octets) describing the reason for closing the connection.\n\n* `write-to-client-ping (client \u0026optional message)`: Send a `ping` frame to `client` according to RFC 6455. `message` is optional payload data (max 125 bytes). The client must respond with a pong frame containing the same payload data. Useful for connection keep-alive and latency testing.\n\n## Getting information about connected clients  \n   (most of these should be treated as read-only, and any visible `setf`\n functions may go away at some point)\n\n* `client-resource-name (client)`: Name of resource requested by `client`.\n\n* `client-query-string (client)`: Query string (the part of the URI after #\\? if any) provided by `client` when connecting.  \n  For example if client connects to `ws://example.com/test?foo`, `client-resource-name` would return `\"/test\"` and `client-query-string` would return `\"foo\"`.\n\n* `client-websocket-version (client)`: Protocol version being used for specified `client`.\n\n* `client-host (client)`: IP address of client, as a string.\n\n* `client-port (client)`: Port from which client connected.\n\n* `client-connection-headers (client)`: Hash table containing any HTTP headers supplied by `client`, as a hash table of keywords (`:user-agent`, `:cookie`, etc) -\u003e strings.\n\n\n\n\n","funding_links":[],"categories":["Interfaces to other package managers"],"sub_categories":["Hosting platforms"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F3b%2Fclws","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F3b%2Fclws","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F3b%2Fclws/lists"}