@subiz/wsclient 3.1.1 → 3.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierrc.js +13 -1
- package/package.json +3 -4
- package/realtime.js +48 -36
- package/realtime.test.js +10 -52
- package/.eslintrc.js +0 -197
- package/test/client_test.js +0 -161
- package/test/test_server.js +0 -140
package/.prettierrc.js
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
module.exports = {
|
|
2
|
+
arrowParens: 'always',
|
|
2
3
|
bracketSpacing: false,
|
|
4
|
+
embeddedLanguageFormatting: 'auto',
|
|
5
|
+
htmlWhitespaceSensitivity: 'css',
|
|
6
|
+
insertPragma: false,
|
|
3
7
|
jsxBracketSameLine: true,
|
|
8
|
+
jsxSingleQuote: true,
|
|
9
|
+
printWidth: 120,
|
|
10
|
+
proseWrap: 'preserve',
|
|
11
|
+
quoteProps: 'as-needed',
|
|
12
|
+
requirePragma: false,
|
|
13
|
+
semi: false,
|
|
4
14
|
singleQuote: true,
|
|
15
|
+
tabWidth: 2,
|
|
5
16
|
trailingComma: 'all',
|
|
6
|
-
|
|
17
|
+
useTabs: true,
|
|
18
|
+
vueIndentScriptAndStyle: false,
|
|
7
19
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@subiz/wsclient",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.7",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node realtime.test.js"
|
|
@@ -14,10 +14,9 @@
|
|
|
14
14
|
"homepage": "https://github.com/subiz/wsclient#readme",
|
|
15
15
|
"description": "",
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"
|
|
18
|
-
"tape": "^5.0.1"
|
|
17
|
+
"tape": "^5.3.1"
|
|
19
18
|
},
|
|
20
19
|
"dependencies": {
|
|
21
|
-
"@subiz/flow": "^1.0.
|
|
20
|
+
"@subiz/flow": "^1.0.9"
|
|
22
21
|
}
|
|
23
22
|
}
|
package/realtime.js
CHANGED
|
@@ -13,7 +13,7 @@ var flow = require('@subiz/flow')
|
|
|
13
13
|
//
|
|
14
14
|
// Once dead, onDead will be called. After that, the connection is
|
|
15
15
|
// useless, all resources will be released.
|
|
16
|
-
function Conn
|
|
16
|
+
function Conn(apiUrl, credential, onDead, onEvents, callAPI) {
|
|
17
17
|
callAPI = callAPI || xhrsend // allow hook
|
|
18
18
|
credential = credential || {}
|
|
19
19
|
// tell if connection is dead
|
|
@@ -22,7 +22,7 @@ function Conn (apiUrl, credential, onDead, onEvents, callAPI) {
|
|
|
22
22
|
var lastToken = ''
|
|
23
23
|
|
|
24
24
|
// kill force the connection to dead state
|
|
25
|
-
this.kill = function
|
|
25
|
+
this.kill = function() {
|
|
26
26
|
dead = true
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -31,9 +31,9 @@ function Conn (apiUrl, credential, onDead, onEvents, callAPI) {
|
|
|
31
31
|
// exponential backoff algorithm. Polling loop will terminate if it encounter an
|
|
32
32
|
// un-retryable error
|
|
33
33
|
// the polling loop starts after the first subscribe call finished successfully
|
|
34
|
-
var polling = function
|
|
34
|
+
var polling = function(backoff) {
|
|
35
35
|
if (dead) return
|
|
36
|
-
callAPI('get', apiUrl + 'poll?token=' + lastToken, undefined, function
|
|
36
|
+
callAPI('get', apiUrl + 'poll?token=' + lastToken, undefined, function(body, code) {
|
|
37
37
|
if (dead) return
|
|
38
38
|
if (retryable(code)) return setTimeout(polling, calcNextBackoff(backoff), backoff + 1)
|
|
39
39
|
if (code !== 200) {
|
|
@@ -55,20 +55,20 @@ function Conn (apiUrl, credential, onDead, onEvents, callAPI) {
|
|
|
55
55
|
})
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
var subQueue = flow.batch(50, 100, function
|
|
58
|
+
var subQueue = flow.batch(50, 100, function(events) {
|
|
59
59
|
if (dead) return ['dead']
|
|
60
60
|
if (events.length <= 0) return []
|
|
61
61
|
|
|
62
62
|
var out = []
|
|
63
|
-
return flow
|
|
64
|
-
loop(function
|
|
65
|
-
return new Promise(function
|
|
63
|
+
return flow
|
|
64
|
+
.loop(function() {
|
|
65
|
+
return new Promise(function(rs) {
|
|
66
66
|
var query = '?token=' + lastToken
|
|
67
|
-
credential.getAccessToken().then(function
|
|
67
|
+
credential.getAccessToken().then(function(access_token) {
|
|
68
68
|
if (credential.user_mask) query += '&user-mask=' + encodeURIComponent(credential.user_mask)
|
|
69
69
|
else if (access_token) query += '&access-token=' + access_token
|
|
70
70
|
|
|
71
|
-
callAPI('post', apiUrl + 'subs' + query, JSON.stringify({
|
|
71
|
+
callAPI('post', apiUrl + 'subs' + query, JSON.stringify({events: events}), function(body, code) {
|
|
72
72
|
if (dead) {
|
|
73
73
|
out = repeat('dead', events.length)
|
|
74
74
|
return rs(false) // break loop
|
|
@@ -100,8 +100,8 @@ function Conn (apiUrl, credential, onDead, onEvents, callAPI) {
|
|
|
100
100
|
})
|
|
101
101
|
})
|
|
102
102
|
})
|
|
103
|
-
})
|
|
104
|
-
then(function
|
|
103
|
+
})
|
|
104
|
+
.then(function() {
|
|
105
105
|
return out
|
|
106
106
|
})
|
|
107
107
|
})
|
|
@@ -119,11 +119,11 @@ function Conn (apiUrl, credential, onDead, onEvents, callAPI) {
|
|
|
119
119
|
// additional features compare to conn:
|
|
120
120
|
// + auto recreate and resub if the last conn is dead
|
|
121
121
|
// + don't subscribe already subscribed events
|
|
122
|
-
function Realtime
|
|
122
|
+
function Realtime(apiUrls, credential, callAPI) {
|
|
123
123
|
if (typeof apiUrls === 'string' || apiUrls instanceof String) apiUrls = [apiUrls]
|
|
124
124
|
credential = credential || {}
|
|
125
125
|
if (!credential.getAccessToken) {
|
|
126
|
-
credential.getAccessToken = function
|
|
126
|
+
credential.getAccessToken = function() {
|
|
127
127
|
return Promise.resolve('')
|
|
128
128
|
}
|
|
129
129
|
}
|
|
@@ -140,36 +140,36 @@ function Realtime (apiUrls, credential, callAPI) {
|
|
|
140
140
|
|
|
141
141
|
// stop the connection
|
|
142
142
|
var stop = false
|
|
143
|
-
this.stop = function
|
|
143
|
+
this.stop = function() {
|
|
144
144
|
if (stop) return
|
|
145
145
|
stop = true
|
|
146
146
|
conn.kill()
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
this.onEvent = function
|
|
149
|
+
this.onEvent = function(cb) {
|
|
150
150
|
return pubsub.on('event', cb)
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
this.onInterrupted = function
|
|
153
|
+
this.onInterrupted = function(cb) {
|
|
154
154
|
return pubsub.on('interrupted', cb)
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
var conn
|
|
158
|
-
this.subscribe = function
|
|
158
|
+
this.subscribe = function(events) {
|
|
159
159
|
if (stop) return Promise.resolve({})
|
|
160
160
|
if (typeof events === 'string') events = [events]
|
|
161
|
-
if (!Array.isArray(events)) return Promise.resolve({
|
|
161
|
+
if (!Array.isArray(events)) return Promise.resolve({error: 'param should be an array or string'})
|
|
162
162
|
|
|
163
163
|
// ignore already subscribed events
|
|
164
164
|
var all = []
|
|
165
165
|
for (var i = 0; i < events.length; i++) {
|
|
166
166
|
var topic = events[i]
|
|
167
167
|
if (!topic) continue
|
|
168
|
-
if (topics[topic]
|
|
168
|
+
if (!topics[topic]) all.push(conn.subscribe(topic))
|
|
169
169
|
}
|
|
170
170
|
if (all.length === 0) return Promise.resolve({})
|
|
171
|
-
return Promise.all(all).then(function
|
|
172
|
-
for (var i = 0; i < errs.length; i++) if (errs[i]) return {
|
|
171
|
+
return Promise.all(all).then(function(errs) {
|
|
172
|
+
for (var i = 0; i < errs.length; i++) if (errs[i]) return {error: errs[i]}
|
|
173
173
|
for (var j = 0; j < events.length; j++) topics[events[j]] = true
|
|
174
174
|
return {}
|
|
175
175
|
})
|
|
@@ -177,7 +177,7 @@ function Realtime (apiUrls, credential, callAPI) {
|
|
|
177
177
|
|
|
178
178
|
// reconnect make sure there is alway a Conn running in the background
|
|
179
179
|
// if the Conn is dead, it recreate a new one
|
|
180
|
-
var reconnect = function
|
|
180
|
+
var reconnect = function() {
|
|
181
181
|
if (stop) return
|
|
182
182
|
// reset subscribed topic
|
|
183
183
|
var allTopics = Object.keys(topics)
|
|
@@ -187,12 +187,12 @@ function Realtime (apiUrls, credential, callAPI) {
|
|
|
187
187
|
conn = new Conn(
|
|
188
188
|
randomUrl,
|
|
189
189
|
credential,
|
|
190
|
-
function
|
|
190
|
+
function(code, body, status) {
|
|
191
191
|
if (stop) return
|
|
192
192
|
pubsub.emit('interrupted', code, body, status)
|
|
193
193
|
setTimeout(reconnect, 2000) // reconnect and resubscribe after 2 sec
|
|
194
194
|
},
|
|
195
|
-
function
|
|
195
|
+
function(topics) {
|
|
196
196
|
if (stop) return
|
|
197
197
|
for (var i = 0; i < topics.length; i++) pubsub.emit('event', topics[i])
|
|
198
198
|
},
|
|
@@ -206,16 +206,28 @@ function Realtime (apiUrls, credential, callAPI) {
|
|
|
206
206
|
reconnect()
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
+
var dofetch = function(method, url, body, cb) {
|
|
210
|
+
let headers = {}
|
|
211
|
+
if (body) headers['content-type'] = 'text/plain'
|
|
212
|
+
fetch(url, {
|
|
213
|
+
method: method,
|
|
214
|
+
headers: headers,
|
|
215
|
+
body: body,
|
|
216
|
+
})
|
|
217
|
+
.then((res) => res.text().then((text) => cb && cb(text, res.status)))
|
|
218
|
+
.catch((err) => cb && cb(err, -1))
|
|
219
|
+
}
|
|
209
220
|
// xhrsend sends an HTTP request
|
|
210
|
-
|
|
221
|
+
var xhrsend = function(method, url, body, cb) {
|
|
222
|
+
if (typeof XMLHttpRequest === 'undefined') return dofetch(method, url, body, cb)
|
|
211
223
|
var request = new XMLHttpRequest()
|
|
212
|
-
request.onreadystatechange = function
|
|
224
|
+
request.onreadystatechange = function(e) {
|
|
213
225
|
if (request.readyState !== 4) return
|
|
214
226
|
cb && cb(request.responseText, request.status)
|
|
215
227
|
cb = undefined // dont call cb anymore
|
|
216
228
|
}
|
|
217
229
|
|
|
218
|
-
request.onerror = function
|
|
230
|
+
request.onerror = function() {
|
|
219
231
|
cb && cb(request.responseText, -1) // network error
|
|
220
232
|
cb = undefined // dont call cb anymore
|
|
221
233
|
}
|
|
@@ -227,13 +239,13 @@ function xhrsend (method, url, body, cb) {
|
|
|
227
239
|
|
|
228
240
|
// JSON.parse without exeption
|
|
229
241
|
// return undefined when parsing invalid JSON string
|
|
230
|
-
function parseJSON
|
|
242
|
+
function parseJSON(str) {
|
|
231
243
|
try {
|
|
232
244
|
return JSON.parse(str)
|
|
233
245
|
} catch (e) {}
|
|
234
246
|
}
|
|
235
247
|
|
|
236
|
-
function Pubsub
|
|
248
|
+
function Pubsub() {
|
|
237
249
|
// holds all subscriptions for all topics
|
|
238
250
|
// example {
|
|
239
251
|
// topic1: [cb1, cb2],
|
|
@@ -243,7 +255,7 @@ function Pubsub () {
|
|
|
243
255
|
|
|
244
256
|
// emit notifies any callback functions that previously
|
|
245
257
|
// subscribed to the topic (by the on function)
|
|
246
|
-
this.emit = function
|
|
258
|
+
this.emit = function(topic) {
|
|
247
259
|
// skip the first item in arguments, which is the topic
|
|
248
260
|
var args = []
|
|
249
261
|
for (var i = 1; i < arguments.length; i++) args.push(arguments[i])
|
|
@@ -256,7 +268,7 @@ function Pubsub () {
|
|
|
256
268
|
// register a callback function for a topic
|
|
257
269
|
// when something is sent to the topic (by the emit function),
|
|
258
270
|
// the callback function will be called
|
|
259
|
-
this.on = function
|
|
271
|
+
this.on = function(topic, cb) {
|
|
260
272
|
if (!listeners[topic]) listeners[topic] = []
|
|
261
273
|
listeners[topic].push(cb)
|
|
262
274
|
}
|
|
@@ -266,7 +278,7 @@ function Pubsub () {
|
|
|
266
278
|
// before sending a next request
|
|
267
279
|
// the results is determind based on exponential backoff algorithm
|
|
268
280
|
// see https://en.wikipedia.org/wiki/Exponential_backoff
|
|
269
|
-
function calcNextBackoff
|
|
281
|
+
function calcNextBackoff(attempts) {
|
|
270
282
|
var RECONNECT_INTERVAL = 1000
|
|
271
283
|
var MAX_RECONNECT_INTERVAL = 10000
|
|
272
284
|
var RECONNECT_DECAY = 1.5
|
|
@@ -278,19 +290,19 @@ function calcNextBackoff (attempts) {
|
|
|
278
290
|
|
|
279
291
|
// retryable decides whether we should resent the HTTP request
|
|
280
292
|
// based on last response or network state
|
|
281
|
-
function retryable
|
|
293
|
+
function retryable(code) {
|
|
282
294
|
// code = 0 means server return response without CORS header, so we are unable to read it
|
|
283
295
|
// code = -1 means the network connection is dead
|
|
284
296
|
return (code >= 500 && code < 600) || code === 0 || code === -1 || code === 429
|
|
285
297
|
}
|
|
286
298
|
|
|
287
|
-
function repeat
|
|
299
|
+
function repeat(ele, n) {
|
|
288
300
|
var arr = []
|
|
289
301
|
for (var i = 0; i < n; i++) arr.push(ele)
|
|
290
302
|
return arr
|
|
291
303
|
}
|
|
292
304
|
|
|
293
|
-
function absUrl
|
|
305
|
+
function absUrl(url) {
|
|
294
306
|
url = url || ''
|
|
295
307
|
if (url.startsWith('//')) return 'https:' + url
|
|
296
308
|
return url
|
package/realtime.test.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
var test = require('tape')
|
|
2
|
-
const axios = require('axios')
|
|
3
2
|
var Realtime = require('./realtime.js')
|
|
4
3
|
|
|
5
|
-
function makeMockServer
|
|
4
|
+
function makeMockServer(script) {
|
|
6
5
|
var i = -1
|
|
7
6
|
return (method, url, body, cb) => {
|
|
8
7
|
i++
|
|
@@ -10,32 +9,7 @@ function makeMockServer (script) {
|
|
|
10
9
|
}
|
|
11
10
|
}
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
method = method.toLowerCase()
|
|
15
|
-
return axios[method](url, body).then(resp => cb(resp.rawData, resp.statusCode))
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
test.skip('realtime simple', async t => {
|
|
19
|
-
// var accid = 'acqcsmrppbftadjzxnvo'
|
|
20
|
-
var userid = 'usqnkpwrkrszjgyfajemn'
|
|
21
|
-
var mask = 'acqcsmrppbftadjzxnvo@usqnkpwrkrszjgyfajemn@Iqz40aZ4dwQuvFn8kJA6gqKnyyQLFA0xU44Emw=='
|
|
22
|
-
|
|
23
|
-
var realtime = new Realtime({ user_mask: mask }, axiosCall)
|
|
24
|
-
var evid = '-'
|
|
25
|
-
realtime.onEvent(ev => {
|
|
26
|
-
if (ev.data.message.id === evid) {
|
|
27
|
-
realtime.stop()
|
|
28
|
-
t.end()
|
|
29
|
-
}
|
|
30
|
-
})
|
|
31
|
-
realtime.onInterrupted(() => {})
|
|
32
|
-
var err = await realtime.subscribe(['message_pong.account.acqcsmrppbftadjzxnvo.user.usqnkpwrkrszjgyfajemn'])
|
|
33
|
-
t.equal(err, undefined)
|
|
34
|
-
var res = await sendMsg(userid, mask)
|
|
35
|
-
evid = res.data.id
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
test('realtime', async t => {
|
|
12
|
+
test('realtime', async (t) => {
|
|
39
13
|
var callapi = makeMockServer([
|
|
40
14
|
async (method, url, body, cb) => {
|
|
41
15
|
// must first call sub and must attach user-mask in the query
|
|
@@ -58,16 +32,15 @@ test('realtime', async t => {
|
|
|
58
32
|
t.true(url.indexOf('token=token1') !== -1)
|
|
59
33
|
realtime.stop()
|
|
60
34
|
cb(`{"events": [], "token": "-"}`, 200)
|
|
61
|
-
t.end()
|
|
62
35
|
},
|
|
63
36
|
])
|
|
64
37
|
|
|
65
|
-
var realtime = new Realtime('', {
|
|
38
|
+
var realtime = new Realtime('', {user_mask: 'mask123'}, callapi)
|
|
66
39
|
var err = await realtime.subscribe(['message_pong'])
|
|
67
|
-
t.
|
|
40
|
+
t.true(Object.keys(err).length === 0)
|
|
68
41
|
})
|
|
69
42
|
|
|
70
|
-
test('realtime subs when server down and then backup', async t => {
|
|
43
|
+
test('realtime subs when server down and then backup', async (t) => {
|
|
71
44
|
var callapi = makeMockServer([
|
|
72
45
|
async (method, url, body, cb) => {
|
|
73
46
|
// must first call sub and must attach user-mask in the query
|
|
@@ -96,16 +69,15 @@ test('realtime subs when server down and then backup', async t => {
|
|
|
96
69
|
t.true(url.indexOf('token=token0') !== -1)
|
|
97
70
|
realtime.stop()
|
|
98
71
|
cb(`{"events": [], "token": "-"}`, 200)
|
|
99
|
-
t.end()
|
|
100
72
|
},
|
|
101
73
|
])
|
|
102
74
|
|
|
103
|
-
var realtime = new Realtime('', {
|
|
75
|
+
var realtime = new Realtime('', {user_mask: 'mask123'}, callapi)
|
|
104
76
|
var err = await realtime.subscribe(['message_pong'])
|
|
105
|
-
t.
|
|
77
|
+
t.true(Object.keys(err).length === 0)
|
|
106
78
|
})
|
|
107
79
|
|
|
108
|
-
test('realtime must stop if there is something wrong with our credential or our code', async t => {
|
|
80
|
+
test('realtime must stop if there is something wrong with our credential or our code', async (t) => {
|
|
109
81
|
var subcode
|
|
110
82
|
var callapi = makeMockServer([
|
|
111
83
|
async (method, url, bkody, cb) => {
|
|
@@ -115,27 +87,13 @@ test('realtime must stop if there is something wrong with our credential or our
|
|
|
115
87
|
t.true(url.indexOf('user-mask=mask123') !== -1)
|
|
116
88
|
cb(`400 invalid mask`, 400)
|
|
117
89
|
realtime.stop()
|
|
118
|
-
t.end()
|
|
119
90
|
},
|
|
120
91
|
])
|
|
121
92
|
|
|
122
|
-
var realtime = new Realtime('', {
|
|
93
|
+
var realtime = new Realtime('', {user_mask: 'mask123'}, callapi)
|
|
123
94
|
realtime.onInterrupted((_, _1, code) => {
|
|
124
95
|
subcode = code
|
|
125
96
|
})
|
|
126
97
|
var err = await realtime.subscribe(['message_pong'])
|
|
127
|
-
t.false(err ===
|
|
98
|
+
t.false(Object.keys(err).length === 0)
|
|
128
99
|
})
|
|
129
|
-
|
|
130
|
-
function sendMsg (userid, mask, text) {
|
|
131
|
-
if (!text) text = 'hello'
|
|
132
|
-
var ev = {
|
|
133
|
-
by: { id: userid, type: 'user' },
|
|
134
|
-
data: { message: { text, format: 'plaintext' } },
|
|
135
|
-
type: 'message_sent',
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
mask = encodeURIComponent(mask)
|
|
139
|
-
var convoid = 'csqohilsfssnkrhnli'
|
|
140
|
-
return axios.post(`https://api.subiz.net/4.0/conversations/${convoid}/messages?x-user-mask=${mask}`, ev)
|
|
141
|
-
}
|
package/.eslintrc.js
DELETED
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
parserOptions: {
|
|
3
|
-
ecmaVersion: 2018,
|
|
4
|
-
ecmaFeatures: {
|
|
5
|
-
jsx: true,
|
|
6
|
-
},
|
|
7
|
-
sourceType: 'module',
|
|
8
|
-
},
|
|
9
|
-
extends: 'eslint:recommended',
|
|
10
|
-
env: {
|
|
11
|
-
es6: true,
|
|
12
|
-
node: true,
|
|
13
|
-
},
|
|
14
|
-
globals: {
|
|
15
|
-
document: false,
|
|
16
|
-
navigator: false,
|
|
17
|
-
window: false,
|
|
18
|
-
test: false,
|
|
19
|
-
xtest: false,
|
|
20
|
-
expect: false,
|
|
21
|
-
import: false,
|
|
22
|
-
require: false,
|
|
23
|
-
},
|
|
24
|
-
rules: {
|
|
25
|
-
'accessor-pairs': 'error',
|
|
26
|
-
'arrow-spacing': ['error', {before: true, after: true}],
|
|
27
|
-
'block-spacing': ['error', 'always'],
|
|
28
|
-
'brace-style': ['error', '1tbs', {allowSingleLine: true}],
|
|
29
|
-
'comma-dangle': ['error', 'always-multiline'],
|
|
30
|
-
'comma-spacing': ['error', {before: false, after: true}],
|
|
31
|
-
'comma-style': ['error', 'last'],
|
|
32
|
-
'constructor-super': 'error',
|
|
33
|
-
curly: ['error', 'multi-line'],
|
|
34
|
-
'dot-location': ['error', 'object'],
|
|
35
|
-
'eol-last': 'error',
|
|
36
|
-
eqeqeq: ['error', 'always', {null: 'ignore'}],
|
|
37
|
-
'func-call-spacing': ['error', 'never'],
|
|
38
|
-
'generator-star-spacing': ['error', {before: true, after: true}],
|
|
39
|
-
'handle-callback-err': ['error', '^(err|error)$'],
|
|
40
|
-
indent: ['error', 'tab'],
|
|
41
|
-
'key-spacing': ['error', {beforeColon: false, afterColon: true}],
|
|
42
|
-
'keyword-spacing': ['error', {before: true, after: true}],
|
|
43
|
-
'new-cap': ['error', {newIsCap: true, capIsNew: false}],
|
|
44
|
-
'new-parens': 'error',
|
|
45
|
-
'no-array-constructor': 'error',
|
|
46
|
-
'no-caller': 'error',
|
|
47
|
-
'no-class-assign': 'error',
|
|
48
|
-
'no-compare-neg-zero': 'error',
|
|
49
|
-
'no-cond-assign': 'error',
|
|
50
|
-
'no-const-assign': 'error',
|
|
51
|
-
'no-constant-condition': ['error', {checkLoops: false}],
|
|
52
|
-
'no-control-regex': 'error',
|
|
53
|
-
'no-debugger': 'error',
|
|
54
|
-
'no-delete-var': 'error',
|
|
55
|
-
'no-dupe-args': 'error',
|
|
56
|
-
'no-dupe-class-members': 'error',
|
|
57
|
-
'no-dupe-keys': 'error',
|
|
58
|
-
'no-duplicate-case': 'error',
|
|
59
|
-
'no-empty-character-class': 'error',
|
|
60
|
-
'no-empty-pattern': 'error',
|
|
61
|
-
'no-eval': 'error',
|
|
62
|
-
'no-ex-assign': 'error',
|
|
63
|
-
'no-extend-native': 'error',
|
|
64
|
-
'no-extra-bind': 'error',
|
|
65
|
-
'no-extra-boolean-cast': 'error',
|
|
66
|
-
'no-extra-parens': ['error', 'functions'],
|
|
67
|
-
'no-fallthrough': 'error',
|
|
68
|
-
'no-floating-decimal': 'error',
|
|
69
|
-
'no-func-assign': 'error',
|
|
70
|
-
'no-global-assign': 'error',
|
|
71
|
-
'no-implied-eval': 'error',
|
|
72
|
-
'no-inner-declarations': ['error', 'functions'],
|
|
73
|
-
'no-invalid-regexp': 'error',
|
|
74
|
-
'no-irregular-whitespace': 'error',
|
|
75
|
-
'no-iterator': 'error',
|
|
76
|
-
'no-label-var': 'error',
|
|
77
|
-
'no-labels': ['error', {allowLoop: false, allowSwitch: false}],
|
|
78
|
-
'no-lone-blocks': 'error',
|
|
79
|
-
'no-mixed-operators': [
|
|
80
|
-
'error',
|
|
81
|
-
{
|
|
82
|
-
groups: [
|
|
83
|
-
['==', '!=', '===', '!==', '>', '>=', '<', '<='],
|
|
84
|
-
['&&', '||'],
|
|
85
|
-
['in', 'instanceof'],
|
|
86
|
-
],
|
|
87
|
-
allowSamePrecedence: true,
|
|
88
|
-
},
|
|
89
|
-
],
|
|
90
|
-
'no-mixed-spaces-and-tabs': 'error',
|
|
91
|
-
'no-multi-spaces': 'error',
|
|
92
|
-
'no-multi-str': 'error',
|
|
93
|
-
'no-multiple-empty-lines': ['error', {max: 1, maxEOF: 0}],
|
|
94
|
-
'no-negated-in-lhs': 'error',
|
|
95
|
-
'no-new': 'error',
|
|
96
|
-
'no-new-func': 'error',
|
|
97
|
-
'no-new-object': 'error',
|
|
98
|
-
'no-new-require': 'error',
|
|
99
|
-
'no-new-symbol': 'error',
|
|
100
|
-
'no-new-wrappers': 'error',
|
|
101
|
-
'no-obj-calls': 'error',
|
|
102
|
-
'no-octal': 'error',
|
|
103
|
-
'no-octal-escape': 'error',
|
|
104
|
-
'no-path-concat': 'error',
|
|
105
|
-
'no-proto': 'error',
|
|
106
|
-
'no-redeclare': 'error',
|
|
107
|
-
'no-regex-spaces': 'error',
|
|
108
|
-
'no-return-assign': ['error', 'except-parens'],
|
|
109
|
-
'no-return-await': 'error',
|
|
110
|
-
'no-self-assign': 'error',
|
|
111
|
-
'no-self-compare': 'error',
|
|
112
|
-
'no-sequences': 'error',
|
|
113
|
-
'no-shadow-restricted-names': 'error',
|
|
114
|
-
'no-sparse-arrays': 'error',
|
|
115
|
-
'no-template-curly-in-string': 'error',
|
|
116
|
-
'no-this-before-super': 'error',
|
|
117
|
-
'no-throw-literal': 'error',
|
|
118
|
-
'no-trailing-spaces': 'error',
|
|
119
|
-
'no-undef': 'error',
|
|
120
|
-
'no-undef-init': 'error',
|
|
121
|
-
'no-unexpected-multiline': 'error',
|
|
122
|
-
'no-unmodified-loop-condition': 'error',
|
|
123
|
-
'no-unneeded-ternary': ['error', {defaultAssignment: false}],
|
|
124
|
-
'no-unreachable': 'error',
|
|
125
|
-
'no-unsafe-finally': 'error',
|
|
126
|
-
'no-unsafe-negation': 'error',
|
|
127
|
-
'no-unused-expressions': [
|
|
128
|
-
'error',
|
|
129
|
-
{
|
|
130
|
-
allowShortCircuit: true,
|
|
131
|
-
allowTernary: true,
|
|
132
|
-
allowTaggedTemplates: true,
|
|
133
|
-
},
|
|
134
|
-
],
|
|
135
|
-
'no-unused-vars': [
|
|
136
|
-
'error',
|
|
137
|
-
{
|
|
138
|
-
vars: 'all',
|
|
139
|
-
args: 'none',
|
|
140
|
-
ignoreRestSiblings: true,
|
|
141
|
-
varsIgnorePattern: '^_',
|
|
142
|
-
},
|
|
143
|
-
],
|
|
144
|
-
'no-use-before-define': [
|
|
145
|
-
'error',
|
|
146
|
-
{functions: false, classes: false, variables: false},
|
|
147
|
-
],
|
|
148
|
-
'no-useless-call': 'error',
|
|
149
|
-
'no-useless-computed-key': 'error',
|
|
150
|
-
'no-useless-constructor': 'error',
|
|
151
|
-
'no-useless-escape': 'error',
|
|
152
|
-
'no-useless-rename': 'error',
|
|
153
|
-
'no-useless-return': 'error',
|
|
154
|
-
'no-whitespace-before-property': 'error',
|
|
155
|
-
'no-with': 'error',
|
|
156
|
-
'object-curly-spacing': ['error', 'always'],
|
|
157
|
-
'object-property-newline': [
|
|
158
|
-
'error',
|
|
159
|
-
{allowMultiplePropertiesPerLine: true},
|
|
160
|
-
],
|
|
161
|
-
'one-var': ['error', {initialized: 'never'}],
|
|
162
|
-
'operator-linebreak': [
|
|
163
|
-
'error',
|
|
164
|
-
'after',
|
|
165
|
-
{overrides: {'?': 'before', ':': 'before'}},
|
|
166
|
-
],
|
|
167
|
-
'padded-blocks': [
|
|
168
|
-
'error',
|
|
169
|
-
{blocks: 'never', switches: 'never', classes: 'never'},
|
|
170
|
-
],
|
|
171
|
-
'prefer-promise-reject-errors': 'error',
|
|
172
|
-
quotes: [
|
|
173
|
-
'error',
|
|
174
|
-
'single',
|
|
175
|
-
{avoidEscape: true, allowTemplateLiterals: true},
|
|
176
|
-
],
|
|
177
|
-
'rest-spread-spacing': ['error', 'never'],
|
|
178
|
-
semi: ['error', 'never'],
|
|
179
|
-
'semi-spacing': ['error', {before: false, after: true}],
|
|
180
|
-
'space-before-blocks': ['error', 'always'],
|
|
181
|
-
'space-before-function-paren': ['error', 'always'],
|
|
182
|
-
'space-in-parens': ['error', 'never'],
|
|
183
|
-
'space-infix-ops': 'error',
|
|
184
|
-
'space-unary-ops': ['error', {words: true, nonwords: false}],
|
|
185
|
-
'symbol-description': 'error',
|
|
186
|
-
'template-curly-spacing': ['error', 'never'],
|
|
187
|
-
'template-tag-spacing': ['error', 'never'],
|
|
188
|
-
'unicode-bom': ['error', 'never'],
|
|
189
|
-
'use-isnan': 'error',
|
|
190
|
-
'valid-typeof': ['error', {requireStringLiterals: true}],
|
|
191
|
-
'wrap-iife': ['error', 'any', {functionPrototypeMethods: true}],
|
|
192
|
-
'yield-star-spacing': ['error', 'both'],
|
|
193
|
-
yoda: ['error', 'never'],
|
|
194
|
-
'max-depth': ['error', {max: 5}],
|
|
195
|
-
'max-len': 'off',
|
|
196
|
-
},
|
|
197
|
-
}
|
package/test/client_test.js
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
var WebSocket = require('ws')
|
|
2
|
-
var client = require('../src/client.js')
|
|
3
|
-
client.env.WebSocket = WebSocket
|
|
4
|
-
|
|
5
|
-
const server = require('./test_server.js').server
|
|
6
|
-
var host = 'ws://localhost:8088'
|
|
7
|
-
function testServerDown2 (done) {
|
|
8
|
-
var r = Math.random()
|
|
9
|
-
var ws = new client.WS({ pickUrl: done => done(`${host}/3/${r}`) })
|
|
10
|
-
|
|
11
|
-
var connid = ''
|
|
12
|
-
var fistconnect = true
|
|
13
|
-
var connected = 0
|
|
14
|
-
ws.onopen = (a, id) => {
|
|
15
|
-
connected++
|
|
16
|
-
connid = id
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
var mescount = 0
|
|
20
|
-
ws.onmessage = (a, mes) => {
|
|
21
|
-
mescount++
|
|
22
|
-
if (mescount == 1) {
|
|
23
|
-
if (connected != 1) {
|
|
24
|
-
throw 'invalid connection id'
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (mes != 1) {
|
|
28
|
-
throw `invalid first message, got ${mes}`
|
|
29
|
-
}
|
|
30
|
-
return
|
|
31
|
-
}
|
|
32
|
-
if (mescount == 2) {
|
|
33
|
-
if (connid != 4) throw 'wrong connid'
|
|
34
|
-
if (connected != 2) throw `wrong connected time, got ${connected}`
|
|
35
|
-
if (closed != 3) throw `wrong closed time, got ${closed}`
|
|
36
|
-
if (errcount != 0) throw `wrong err count, got ${errcount}`
|
|
37
|
-
if (mes != 2) throw `invalid first message, got ${mes}`
|
|
38
|
-
done()
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
var closed = 0
|
|
43
|
-
ws.onclose = () => {
|
|
44
|
-
closed++
|
|
45
|
-
if (closed == 1) {
|
|
46
|
-
if (connected != 1) {
|
|
47
|
-
throw 'invalid state'
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
var errcount = 0
|
|
53
|
-
ws.onerror = (a, err) => {
|
|
54
|
-
errcount++
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function testServerDown (done) {
|
|
59
|
-
var r = Math.random()
|
|
60
|
-
var ws = new client.WS({ pickUrl: done => done(`${host}/2/${r}`) })
|
|
61
|
-
|
|
62
|
-
var connid = ''
|
|
63
|
-
var fistconnect = true
|
|
64
|
-
var connected = 0
|
|
65
|
-
ws.onopen = (a, id) => {
|
|
66
|
-
connected++
|
|
67
|
-
connid = id
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
var mescount = 0
|
|
71
|
-
ws.onmessage = (a, mes) => {
|
|
72
|
-
mescount++
|
|
73
|
-
if (mescount == 1) {
|
|
74
|
-
if (connected != 1) {
|
|
75
|
-
throw 'invalid connection id'
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (mes != 1) {
|
|
79
|
-
throw `invalid first message, got ${mes}`
|
|
80
|
-
}
|
|
81
|
-
return
|
|
82
|
-
}
|
|
83
|
-
if (mescount == 2) {
|
|
84
|
-
if (connid != 5) throw `wrong connid, got ${connid}`
|
|
85
|
-
if (connected != 2) throw `wrong connected time, got ${connected}`
|
|
86
|
-
if (closed != 4) throw `wrong closed time, got ${closed}`
|
|
87
|
-
if (errcount != 1) throw `wrong err count, got ${errcount}`
|
|
88
|
-
if (mes != 2) throw `invalid first message, got ${mes}`
|
|
89
|
-
done()
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
var closed = 0
|
|
94
|
-
ws.onclose = () => {
|
|
95
|
-
closed++
|
|
96
|
-
if (closed == 1) {
|
|
97
|
-
if (connected != 1) {
|
|
98
|
-
throw 'invalid state'
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
var errcount = 0
|
|
104
|
-
ws.onerror = (a, err) => {
|
|
105
|
-
errcount++
|
|
106
|
-
if (errcount == 1) {
|
|
107
|
-
if (err != 'dead') {
|
|
108
|
-
throw `invalid error message ${JSON.stringify(err)}`
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function testNormal (done) {
|
|
115
|
-
var ws = new client.WS({ pickUrl: done => done(`${host}/1`) })
|
|
116
|
-
var connid = ''
|
|
117
|
-
ws.onopen = (a, id) => {
|
|
118
|
-
console.log("a")
|
|
119
|
-
connid = id
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
var first = true
|
|
123
|
-
|
|
124
|
-
ws.onmessage = (a, mes) => {
|
|
125
|
-
if (first) {
|
|
126
|
-
first = false
|
|
127
|
-
if (connid == '') {
|
|
128
|
-
throw 'invalid connection id'
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (mes != 1) {
|
|
132
|
-
throw `invalid first message, got ${mes}`
|
|
133
|
-
}
|
|
134
|
-
return
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (mes != 2) {
|
|
138
|
-
throw `invalid second message, got ${mes}`
|
|
139
|
-
}
|
|
140
|
-
done()
|
|
141
|
-
}
|
|
142
|
-
console.log("b")
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function main () {
|
|
146
|
-
server.listen(8088, () => {
|
|
147
|
-
var done = 0
|
|
148
|
-
function donecheck () {
|
|
149
|
-
done++
|
|
150
|
-
if (done === 3) {
|
|
151
|
-
console.log('PASS')
|
|
152
|
-
process.exit(0)
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
testServerDown(donecheck)
|
|
156
|
-
testServerDown2(donecheck)
|
|
157
|
-
testNormal(donecheck)
|
|
158
|
-
})
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
main()
|
package/test/test_server.js
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
const http = require('http')
|
|
2
|
-
const WebSocket = require('ws')
|
|
3
|
-
const url = require('url')
|
|
4
|
-
const querystring = require('querystring')
|
|
5
|
-
|
|
6
|
-
const server = http.createServer()
|
|
7
|
-
const wss1 = new WebSocket.Server({ noServer: true })
|
|
8
|
-
const wss2 = new WebSocket.Server({ noServer: true })
|
|
9
|
-
const wss3 = new WebSocket.Server({ noServer: true })
|
|
10
|
-
|
|
11
|
-
let counter = {}
|
|
12
|
-
|
|
13
|
-
function getID (path) {
|
|
14
|
-
const pathname = url.parse(path).pathname
|
|
15
|
-
return pathname.split('/')
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
wss1.on('connection', ws => {
|
|
19
|
-
ws.send(
|
|
20
|
-
`{"type": "init", "offset": 0, "data": ${JSON.stringify(
|
|
21
|
-
JSON.stringify({ id: 'aaa' })
|
|
22
|
-
)}}`
|
|
23
|
-
)
|
|
24
|
-
ws.send(`{"offset": 1, "data":"1"}`)
|
|
25
|
-
ws.send(`{"offset": 2, "data":"2"}`)
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
wss2.on('connection', (ws, request) => {
|
|
29
|
-
let [x, no, id] = getID(request.url)
|
|
30
|
-
|
|
31
|
-
if (!counter[id]) counter[id] = 0
|
|
32
|
-
counter[id]++
|
|
33
|
-
|
|
34
|
-
//console.log(`[${id}] new connection`, counter[id])
|
|
35
|
-
|
|
36
|
-
let qs = request.url.split('?')[1] || ''
|
|
37
|
-
const params = querystring.parse(qs)
|
|
38
|
-
|
|
39
|
-
//console.log('params', params)
|
|
40
|
-
|
|
41
|
-
if (counter[id] == 1) {
|
|
42
|
-
ws.send(
|
|
43
|
-
`{"type": "init", "offset": 0, "data": ${JSON.stringify(
|
|
44
|
-
JSON.stringify({ id: counter[id] })
|
|
45
|
-
)}}`
|
|
46
|
-
)
|
|
47
|
-
ws.send(`{"offset": 1, "data":"1"}`)
|
|
48
|
-
ws.terminate()
|
|
49
|
-
return
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (counter[id] < 4) {
|
|
53
|
-
if (!params.connection_id) {
|
|
54
|
-
throw new Error('connection_id not found')
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
ws.terminate()
|
|
58
|
-
return
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (counter[id] === 4) {
|
|
62
|
-
// console.log("server", "dead")
|
|
63
|
-
ws.send(`{"error": "dead"}`)
|
|
64
|
-
return
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (counter[id] === 5) {
|
|
68
|
-
// console.log("server", "heaer")
|
|
69
|
-
if (Object.keys(params).length > 0) {
|
|
70
|
-
throw new Error('query string is not empty')
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
ws.send(
|
|
74
|
-
`{"type": "init", "offset": 0, "data": ${JSON.stringify(
|
|
75
|
-
JSON.stringify({ id: counter[id] })
|
|
76
|
-
)}}`
|
|
77
|
-
)
|
|
78
|
-
ws.send(`{"offset": 1, "data":"2"}`)
|
|
79
|
-
}
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
wss3.on('connection', (ws, request) => {
|
|
83
|
-
let [x, no, id] = getID(request.url)
|
|
84
|
-
|
|
85
|
-
if (!counter[id]) counter[id] = 0
|
|
86
|
-
counter[id]++
|
|
87
|
-
|
|
88
|
-
// console.log(`[${id}] new connection`, counter[id])
|
|
89
|
-
|
|
90
|
-
if (counter[id] == 1) {
|
|
91
|
-
ws.send(
|
|
92
|
-
`{"type": "init", "offset": 0, "data": ${JSON.stringify(
|
|
93
|
-
JSON.stringify({ id: counter[id] })
|
|
94
|
-
)}}`
|
|
95
|
-
)
|
|
96
|
-
ws.send(`{"offset": 1, "data":"1"}`)
|
|
97
|
-
ws.terminate()
|
|
98
|
-
return
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (counter[id] < 4) {
|
|
102
|
-
ws.terminate()
|
|
103
|
-
return
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (counter[id] === 4) {
|
|
107
|
-
ws.send(
|
|
108
|
-
`{"type": "init", "offset": 0, "data": ${JSON.stringify(
|
|
109
|
-
JSON.stringify({ id: counter[id] })
|
|
110
|
-
)}}`
|
|
111
|
-
)
|
|
112
|
-
ws.send(`{"offset": 1, "data":"2"}`)
|
|
113
|
-
}
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
server.on('upgrade', (request, socket, head) => {
|
|
117
|
-
let [x, no, id] = getID(request.url)
|
|
118
|
-
|
|
119
|
-
switch (no) {
|
|
120
|
-
case '1':
|
|
121
|
-
wss1.handleUpgrade(request, socket, head, ws => {
|
|
122
|
-
wss1.emit('connection', ws, request)
|
|
123
|
-
})
|
|
124
|
-
break
|
|
125
|
-
case '2':
|
|
126
|
-
wss2.handleUpgrade(request, socket, head, ws => {
|
|
127
|
-
wss2.emit('connection', ws, request)
|
|
128
|
-
})
|
|
129
|
-
break
|
|
130
|
-
case '3':
|
|
131
|
-
wss3.handleUpgrade(request, socket, head, ws => {
|
|
132
|
-
wss3.emit('connection', ws, request)
|
|
133
|
-
})
|
|
134
|
-
break
|
|
135
|
-
default:
|
|
136
|
-
socket.destroy()
|
|
137
|
-
}
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
exports.server = server
|