anchor.diff:  (defvar *websocket-socket* nil
     "The currently active WebSocket socket")
   
  -(defparameter *websocket-handlers* nil
  +(defparameter *websocket-dispatchers* nil
     "List of handler closures that will be queried for new connections")
   
   (defclass websocket-request (request)
  -  ((handler :accessor websocket-request-handler
  +  ((handler :accessor websocket-resource
               :initform nil
  -            :documentation "Message handler of the current request"))
  -  (:documentation "Subclass of the regular Hunchentoot request"))
  -
  -(defclass websocket-reply (reply) ()
  -  (:documentation "Subclass of the regular Hunchentoot reply (used only for dispatching)"))
  -
  -(defmethod initialize-instance :after ((reply websocket-reply) &rest initargs &key &allow-other-keys)
  -  "initialize-instance :after websocket-reply &rest initargs &key &allow-other-keys => side effect
  -
  -Set the reply's external format to Unix EOL / UTF-8 explicitly."
  +            :documentation "Message handler of the current request")))
  +
  +(defclass websocket-reply (reply) ())
  +
  +
  +(defmethod initialize-instance :after ((reply websocket-reply) &rest initargs)
  +  "Set the reply's external format to Unix EOL / UTF-8 explicitly."
     (declare (ignore initargs))
  -  (setf (reply-external-format reply) (make-external-format :utf8 :eol-style :lf)))
  +  (setf (reply-external-format reply)
  +        (make-external-format :utf8 :eol-style :lf)))
   
   (defclass websocket-acceptor (acceptor)
     ((websocket-timeout :initarg :websocket-timeout
                         :accessor websocket-timeout
                         :initform 60
  -                      :documentation "Custom WebSocket timeout overriding the default usocket one"))
  -  (:default-initargs :request-class 'websocket-request :reply-class 'websocket-reply)
  -  (:documentation "Special WebSocket acceptor to be used in place of the regular Hunchentoot one"))
  +                      :documentation "Custom WebSocket timeout override."))
  +  (:default-initargs :request-class 'websocket-request
  +                     :reply-class 'websocket-reply))
   
   (defclass websocket-ssl-acceptor (websocket-acceptor ssl-acceptor) ()
     (:documentation "Special WebSocket SSL acceptor"))
  
positive.code:   ((key :initarg :key :reader websocket-illegal-key-of
          :initform (required-argument :key)
          :documentation "Original, spurious key argument"))
    (:documentation "Signal in case the client has sent an erroneous handshake key"))
  
  (define-condition websocket-illegal-frame-type (condition)
    ((type :initarg :type :reader websocket-illegal-frame-type-of
           :initform (required-argument :type)
           :documentation "Spurious frame type received"))
    (:documentation "Signal in case the client has sent a spurious frame type"))
  
  (defun integer-octets-32be (number)
    "integer-octets-32be number => unsigned byte array
  
  Transform NUMBER into 32bit big endian octets. Required for the (luckily
  obsoleted) old \"hixie\" and early \"hybi\" WebSocket drafts."
    (let ((result (make-array 4 :element-type '(unsigned-byte 8))))
      (dotimes (index 4 result)
        (let ((position #+little-endian (- 24 (* 8 index)) #-little-endian (* 8 index)))
          (setf (aref result index)
                (ldb (byte 8 position) number))))))
  
  (let ((digit-scanner (create-scanner "[^\\d]"))
        (space-scanner (create-scanner "[^ ]")))
    (defun websocket-keyhasha (key)
      "websocket-keyhash key => unsigned byte array
  
  Process a (legacy) WebSocket key and evaluate to a byte sequence that becomes
  part of the challenge. An error will be signalled in case the key is spurious."
      (let ((number (parse-integer (regex-replace-all digit-scanner key "")
                                   :junk-allowed nil))
            (spaces (length (regex-replace-all space-scanner key ""))))
        (if (or (zerop spaces)
                (not (zerop (mod number spaces))))
            (error 'websocket-illegal-key :initarg key)
            (integer-octets-32be (/ number spaces)))))) ; crack-smoking mac pussies at google want big endian
  
  (defun read-bytes-array (stream number)
    "read-bytes-array stream number => unsigned byte array
  
  Read NUMBER bytes from Chunga STREAM into array and return it."
    (let ((result (make-array number :element-type '(unsigned-byte 8))))
      (dotimes (index number result)
        (setf (aref result index)
              (char-int (read-char* stream t))))))
  
  (defun read-key3 (request)
    "read-key3 request => unsigned byte array
  
  Read eight bytes from REQUEST's content stream and return them as a byte
  array."
    (read-bytes-array (content-stream request) 8))
  
  (defun digest-key (key)
    "digest-key key => unsigned byte array
  
  Evaluates to MD5 digest of KEY sequence."
    (digest-sequence :md5 key))
  
  (defun websocket-uri (request host &optional ssl)
    "websocket-uri request host &optional ssl => URI string
  
  Evaluate arguments to corresponding WebSocket (ws:// or wss://) URL."
    (format nil "~:[ws~;wss~]://~a~a" ssl host (script-name request)))
  
  (defun handle-handshake-legacy (request reply)
    "handle-handshake-legacy request reply => challenge
  
  Legacy (< draft-hybi-02) method of performing a WebSocket handshake. Still in
  use for FF4 (if activated) and many versions of Chromium. Also sets the
  Sec-WebSocket-Origin, Sec-WebSocket-Location and Sec-WebSocket-Protocol response
  headers, if successful. The challenge is sent over the raw TCP socket."
    (prog1
        (let* ((stream (make-in-memory-output-stream)))
          (mapc #'(lambda (key)
                    (write-sequence key stream))
                (list (websocket-keyhash (header-in :sec-websocket-key1 request))
                      (websocket-keyhash (header-in :sec-websocket-key2 request))
                      (read-key3 request)))
          (digest-key (get-output-stream-sequence stream)))
      (setf (header-out :sec-websocket-origin reply) (header-in :origin request)
            (header-out :sec-websocket-location reply) (or (websocket-uri request (header-in :host request)
                                                                          (ssl-p (request-acceptor request))))
            (header-out :sec-websocket-protocol reply) (header-in :sec-websocket-protocol request))))
  
  (defun handle-handshake (request reply)
    "handle-handshake for v13
  
  Also sets the
  Sec-WebSocket-Origin, Sec-WebSocket-Location and Sec-WebSocket-Protocol response
  headers, if successful. The challenge is sent over the raw TCP socket."
    (prog1
      (let ((sec-websocket-key+magic
              (concatenate 'string (header-in :sec-websocket-key request) "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")))
      (setf (header-out :sec-websocket-accept reply)
        (base64:usb8-array-to-base64-string
          (ironclad:digest-sequence 'ironclad:sha1 (ironclad:ascii-string-to-byte-array sec-websocket-key+magic))))
      (setf (header-out :sec-websocket-origin reply) (header-in :origin request)
            (header-out :sec-websocket-location reply) (or (websocket-uri request (header-in :host request)
                                                                          (ssl-p (request-acceptor request))))
            (header-out :sec-websocket-protocol reply) (header-in :sec-websocket-protocol request)))))
  
  (defun websocket-handle-handshake (request reply)
    "websocket-handle-handshake request reply => handshake
  
  Try to determine the WebSocket handshake version by checking REQUEST headers and
  handle accordingly, if possible."
    (handler-case
        (prog1
            (cond ((not (equal "13" (header-in* :sec-websocket-version request)))
                   (error 'websocket-unsupported-version :version (header-in* :sec-websocket-version request)));; (header-in :sec-websocket-version request)))
                  ((header-in :sec-websocket-draft request)
                   (error 'websocket-unsupported-version (header-in :sec-websocket-draft request)))
                  ((header-in :sec-websocket-key request)
                   (handle-handshake request reply))
                  (t (error 'websocket-unsupported-version :version "unknown")))
          (setf
            (return-code* reply) +http-switching-protocols+
            (header-out :upgrade reply) "WebSocket"
            (header-out :connection reply) "Upgrade"
            (content-type* reply) "application/octet-stream"))
      (websocket-illegal-key (condition)
        (hunchentoot-error "Illegal key ~a encountered" (websocket-illegal-key-of condition)))
      (websocket-unsupported-version ()
        (hunchentoot-error "WebSocket handshake failed because of unsupported protocol version"))))
  
  (defun websocket-send-term (&optional (stream *websocket-stream*) (mutex *websocket-stream-mutex*))
    "websocket-send-term &optional stream mutex => side effect
  
  Send the magic WebSocket termination sequence across STREAM."
    (with-lock-held (mutex)
      (write-sequence +websocket-terminator+ stream)
      (force-output stream)))
  
  (defun websocket-send-message (message &optional (stream *websocket-stream*) (mutex *websocket-stream-mutex*))
    "websocket-send-message &optional stream mutex => side effect
  
  Encode MESSAGE as UTF-8 bytes and send it across STREAM in a proper frame."
    (when (> (length message) 0) ; empty message would send terminator
      (log-message* :debug "Going to send websocket message ~a" message)
      (with-lock-held (mutex)
  
negative.code: ;;;; Hunchensocket - websocket.lisp
  ;;;;
  ;;;; Hunchentoot-based WebSocket (draft) implementation Copyright (C)
  ;;;; 2011 Alexander Kahl <e-user@fsfe.org>
  ;;;; 2014 Joao Tavora
  ;;;;
  ;;;; This file is part of Hunchensocket.  Redistribution and use in
  ;;;; source and binary forms, with or without modification, are
  ;;;; permitted provided that the following conditions are met:
  ;;;;
  ;;;;   * Redistributions of source code must retain the above copyright
  ;;;;     notice, this list of conditions and the following disclaimer.
  ;;;;
  ;;;;   * Redistributions in binary form must reproduce the above
  ;;;;     copyright notice, this list of conditions and the following
  ;;;;     disclaimer in the documentation and/or other materials
  ;;;;     provided with the distribution.
  ;;;;
  ;;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
  ;;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  ;;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  