-
Notifications
You must be signed in to change notification settings - Fork 6
/
sse.js
155 lines (143 loc) · 4 KB
/
sse.js
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/**
* 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' }
* });
* ```
*/
const sse = function (opts) {
if (opts == null) {
opts = {};
}
if (opts.retry == null) {
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.
*/
var self = function (req, res) {
const session = self.create(req, res);
if (typeof self.onConnect === 'function') {
self.onConnect(session);
}
return 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 = function (event, msg, path) {
if (path == null) {
path = '';
}
return (() => {
const result = [];
for (let el of Array.from(self.sessions)) {
if (!path) {
result.push(el.emit(event, msg));
} else if (el.req.path === path) {
result.push(el.emit(event, msg));
} else {
result.push(undefined);
}
}
return result;
})();
};
/**
* Create a sse session.
* @param {http.IncomingMessage} req Also supports Express.js.
* @param {http.ServerResponse} res Also supports Express.js.
* @return {SSESession}
*/
self.create = function (req, res) {
/**
* A session object is something like:
* ```js
* {
* req, // The http req object.
* res // The http res object.
* }
* ```
*/
const 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 = function (event, msg) {
if (msg == null) {
msg = '';
}
msg = JSON.stringify(msg);
return 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');
return session;
};
return self;
};
module.exports = sse;