forked from ysmood/nokit
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsse.coffee
127 lines (115 loc) · 3.36 KB
/
sse.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
###*
* A Server-Sent Event Manager.
* For more info see [Using server-sent events](https://developer.mozilla.org/en-US/docs/Server-sentEvents/UsingServer-sentEvents).
* It is used to implement the live-reload of web assets.
* @param {Object} opts Defaults:
* ```js
* {
* // The reconnection time to use when attempting to send the event, unit is ms.
* retry: 1000
* }
* ```
* @example
* Your server side code may look like this:
* ```js
* let http = require('http');
* let kit = require('nokit');
* let sse = kit.require('sse');
* let sseHandler = sse();
*
* sseHandler.onConnect = ({ req }) => {
* console.log('client connected: ', req.url)
* }
*
* http.createServer((req, res) => {
* if (req.url === '/sse')
* sseHandler(req, res);
* else
* res.end();
* }).listen(8080, () =>
* setTimeout(() =>
* sseHandler.emit('test', { test: 'ok' })
* );
* );
* ```
*
* You browser code should be something like this:
* ```js
* let es = new EventSource('/sse');
* es.addEventListener('test', (e) => {
* let msg = JSON.parse(e.data);
* console.log(msg); // => { test: 'ok' }
* });
* ```
###
sse = (opts = {}) ->
opts.retry ?= 1000
###*
* The sse middleware for http handler.
* @param {http.IncomingMessage} req Also supports Express.js.
* @param {http.ServerResponse} res Also supports Express.js.
###
self = (req, res) ->
session = self.create req, res
self.onConnect?(session)
self.sessions.push session
###*
* The sessions of connected clients.
* @type {Array}
###
self.sessions = []
###*
* Broadcast a event to all clients.
* @param {String} event The event name.
* @param {Object | String} msg The data you want to emit to session.
* @param {String} [path] The namespace of target sessions. If not set,
* broadcast to all clients.
###
self.emit = (event, msg, path = '') ->
for el in self.sessions
if not path
el.emit event, msg
else if el.req.path == path
el.emit event, msg
###*
* Create a sse session.
* @param {http.IncomingMessage} req Also supports Express.js.
* @param {http.ServerResponse} res Also supports Express.js.
* @return {SSESession}
###
self.create = (req, res) ->
###*
* A session object is something like:
* ```js
* {
* req, // The http req object.
* res // The http res object.
* }
* ```
###
session = { req, res }
req.socket.setTimeout 0
res.writeHead 200, {
'Content-Type': 'text/event-stream'
'Cache-Control': 'no-cache'
'Connection': 'keep-alive'
}
###*
* Emit message to client.
* @param {String} event The event name.
* @param {Object | String} msg The message to send to the client.
###
session.emit = (event, msg = '') ->
msg = JSON.stringify msg
res.write """
id: #{Date.now()}
event: #{event}
retry: #{opts.retry}
data: #{msg}\n\n
"""
req.on 'close', ->
self.sessions.splice (self.sessions.indexOf session), 1
session.emit 'connect', 'ok'
session
self
module.exports = sse