git.fiddlerwoaroof.com
Browse code

(init)

Ed Langley authored on 22/10/2018 06:33:47
Showing 2 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,2 @@
1
+*.fasl
2
+*~
0 3
new file mode 100644
... ...
@@ -0,0 +1,84 @@
1
+(defpackage :fwoar.web-test
2
+  (:use :cl )
3
+  (:export ))
4
+(in-package :fwoar.web-test)
5
+
6
+(defclass websocket-server (hunchensocket:websocket-acceptor
7
+                            hunchentoot:easy-acceptor)
8
+  ())
9
+
10
+(defgeneric find-websocket-resource (acceptor request))
11
+
12
+(defmethod hunchentoot:acceptor-dispatch-request ((acceptor websocket-server)
13
+                                                  (request hunchensocket::websocket-request))
14
+  "Attempt WebSocket connection, else fall back to HTTP"
15
+  (cond ((and (member "upgrade" (cl-ppcre:split "\\s*,\\s*" (hunchentoot:header-in* :connection))
16
+                      :test #'string-equal)
17
+              (string= "websocket" (string-downcase (hunchentoot:header-in* :upgrade))))
18
+         (cond ((setf (hunchensocket:websocket-resource hunchentoot:*request*)
19
+                      (find-websocket-resource acceptor hunchentoot:*request*))
20
+                ;; Found the websocket resource
21
+                (hunchensocket::handle-handshake acceptor hunchentoot:*request* hunchentoot:*reply*)
22
+                ;; HACK! the empty string is also important because if there's
23
+                ;; no content Hunchentoot will declare the connection closed and
24
+                ;; set "Connection: Closed". But there can't be any actual
25
+                ;; content since otherwise it will piggyback onto the first
26
+                ;; websocket frame, which is interpreted as invalid by the
27
+                ;; client. It's also forbidden by the HTTP RFC2616:
28
+                ;;
29
+                ;;    [...] All 1xx (informational), 204 (no content), and 304
30
+                ;;    (not modified) responses MUST NOT include a
31
+                ;;    message-body. [...]
32
+                ;;
33
+                ;; There is a slight non-conformance here: this trick makes
34
+                ;; Hunchentoot send "Content-length: 0". Most browsers don't
35
+                ;; seem to care, but RFC2616 kind of implies that is forbidden,
36
+                ;; since it says the
37
+                ;;
38
+                ;;    [...] the presence of a message-body is signaled by the
39
+                ;;    inclusion of a Content-Length or Transfer-Encoding header
40
+                ;;    field in the requests's message-headers [...]
41
+                ;;
42
+                ;; Note however, we're sending a response, not a request.
43
+                ;;
44
+                (values "" nil nil))
45
+               (t
46
+                ;; Didn't find the websocket-specific resource, return 404.
47
+                (setf (hunchentoot:return-code hunchentoot:*reply*)
48
+                      hunchentoot:+http-not-found+)
49
+                (values nil nil nil))))
50
+        (t
51
+         ;; Client is not requesting websockets, let Hunchentoot do its HTTP
52
+         ;; thing undisturbed.
53
+         (call-next-method))))
54
+
55
+(defclass my-server (websocket-server)
56
+  ())
57
+
58
+(defclass chat-room (hunchensocket:websocket-resource)
59
+  ((name :initarg :name :initform (error "Name this room!") :reader name))
60
+  (:default-initargs :client-class 'user))
61
+
62
+(defclass user (hunchensocket:websocket-client)
63
+  ((name :initarg :user-agent :reader name :initform (error "Name this user!"))))
64
+
65
+(defvar *chat-rooms*
66
+  (list (make-instance 'chat-room :name "/bongo")
67
+        (make-instance 'chat-room :name "/fury")))
68
+
69
+(defmethod find-websocket-resource ((acceptor my-server) request)
70
+  (find (hunchentoot:script-name request)
71
+        *chat-rooms* :test #'string= :key #'name))
72
+
73
+(defun broadcast (room message &rest args)
74
+  (loop for peer in (hunchensocket:clients room)
75
+     do (hunchensocket:send-text-message peer (apply #'format nil message args))))
76
+
77
+(defmethod hunchensocket:client-connected ((room chat-room) user)
78
+  (broadcast room "~a has joined ~a" (name user) (name room)))
79
+
80
+(defmethod hunchensocket:client-disconnected ((room chat-room) user)
81
+  (broadcast room "~a has left ~a" (name user) (name room)))
82
+
83
+(defmethod hunchensocket:text-message-received ((room chat-room) user message)
84
+  (broadcast room "~a says ~a" (name user) message))