Guest User

async SSE server

a guest
Sep 14th, 2013
119
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.15 KB | None | 0 0
  1. (ql:quickload (list :cl-ppcre :cl-async :cl-who :parenscript :babel))
  2. (defpackage :ts-async
  3. (:use :cl :cl-ppcre :cl-async :cl-who :parenscript)
  4. (:import-from :babel #:octets-to-string))
  5. (in-package :ts-async)
  6.  
  7. ;; Requirements:
  8. ;; apt-get install libevent-core-2.0 libevent-extra-2.0
  9.  
  10. (defparameter *channel* nil)
  11.  
  12. (defun hello-server ()
  13. (format t "Starting server.~%")
  14. (as:tcp-server "0.0.0.0" 3000
  15. (lambda (socket data)
  16. (let ((req (parse (octets-to-string data))))
  17. (format t "~s~%~%" req)
  18. (cond ((string= "/sub" req)
  19. (subscribe! socket))
  20. ((string= "/pub" req)
  21. (publish! "Got a message!")
  22. (write/close socket (cat "HTTP/1.1 200 OK" crlf
  23. "Content-Type: text/plain; charset=UTF-8" crlf
  24. "Cache-Control: no-cache, no-store, must-revalidate" crlf
  25. "Content-Length: 10" crlf crlf
  26. "Published!" crlf crlf)))
  27. ((string= "/test" req)
  28. (write/close socket test-page))
  29. (t
  30. (write/close socket (cat "HTTP/1.1 200 OK" crlf
  31. "Content-Type: text/plain; charset=UTF-9" crlf
  32. "Content-Length: 2" crlf crlf
  33. "Ok" crlf crlf))))))
  34. (lambda (err)
  35. (format t "listener event: ~a" err)))
  36. (as:signal-handler 2 (lambda (sig)
  37. (declare (ignore sig))
  38. (as:exit-event-loop))))
  39.  
  40. (defparameter day-names '("Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun"))
  41. (defparameter month-names '("Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"))
  42.  
  43. (defun http-date ()
  44. (multiple-value-bind (second minute hour date month year day-of-week dst-p tz)
  45. (get-decoded-time)
  46. (format nil "~a, ~a ~a ~a ~a:~a:~a GMT~@d"
  47. (nth day-of-week day-names) date
  48. (nth month month-names) year hour minute second (- tz))))
  49.  
  50. (defun subscribe! (sock)
  51. (write/keep-alive
  52. sock (cat "HTTP/1.1 200 OK" crlf
  53. "Date: " (http-date) crlf
  54. "Content-Type: text/event-stream; charset=utf-8" crlf
  55. "Transfer-Encoding: chunked" crlf
  56. "Connection: keep-alive" crlf
  57. "Expires: Thu, 01 Jan 1970 00:00:01 GMT" crlf
  58. "Cache-Control: no-cache, no-store, must-revalidate" crlf crlf))
  59. (push sock *channel*))
  60.  
  61. (defun publish! (msg)
  62. (loop for sock in *channel*
  63. do (handler-case
  64. (if (as:socket-closed sock)
  65. (setf *channel* (remove sock *channel*))
  66. (write/keep-alive sock (cat (format nil "data: ~a" msg) crlf crlf)))
  67. (error (e)
  68. (declare (ignore e))
  69. (format t "Removing inactive socket...")
  70. (setf *channel* (remove sock *channel*))))))
  71.  
  72. (defun write/keep-alive (sock data)
  73. (unless (as:socket-closed sock)
  74. (as:write-socket-data sock data)))
  75.  
  76. (defun write/close (sock data)
  77. (unless (as:socket-closed sock)
  78. (as:write-socket-data
  79. sock data
  80. :write-cb (lambda (sock)
  81. (setf (as:socket-data sock) nil)
  82. (as:close-socket sock)))))
  83.  
  84. (defun start () (as:start-event-loop #'hello-server))
  85.  
  86. (defmethod parse ((req string))
  87. (let ((lines (split "\\r?\\n" req)))
  88. (second (split " " (first lines)))))
  89.  
  90. ;;;;;;;;;; General purpose utilities
  91. (defun cat (&rest seqs)
  92. (apply #'concatenate 'string seqs))
  93. (defconstant crlf (list #\return #\linefeed))
  94. (defparameter test-page
  95. (let ((content (with-html-output-to-string (str nil :prologue t)
  96. (:html
  97. (:head (:title "Test page"))
  98. (:body
  99. (:div :id "console")
  100. (:script
  101. :type "text/javascript"
  102. (str (ps (defvar src (new (-event-source "/sub")))
  103. (defun p (msg)
  104. (let ((elem (chain document (get-element-by-id "console"))))
  105. (setf (@ elem inner-h-t-m-l)
  106. (+ (@ elem inner-h-t-m-l) "<p>" msg "</p>"))))
  107. (setf (@ src onerror)
  108. (lambda (e)
  109. (p "ERROR OCCURRED...")
  110. (p (chain -j-s-o-n (stringify e))))
  111. (@ src onopen)
  112. (lambda (e) (p "STREAM OPENED..."))
  113. (@ src onmessage)
  114. (lambda (e) (p "GOT MESSAGE!")))))))))))
  115. (cat "HTTP/1.1 200 OK" crlf
  116. "Content-Type: text/html; charset=UTF-8" crlf
  117. "Cache-Control: no-cache, no-store, must-revalidate" crlf
  118. "Content-Length: " (write-to-string (length content)) crlf crlf
  119. content crlf crlf)))
Advertisement
Add Comment
Please, Sign In to add comment