diff --git a/packages/@jsonql/ws/src/core/create-websocket-binding/bind-framework-to-jsonql.js b/packages/@jsonql/ws/src/core/create-websocket-binding/bind-framework-to-jsonql.js index a79d94dfd328fc909c7803b6ad26bbc5975c57b2..1b5216a7a6c6c135a2081c1d6c9b0464fb12b93a 100644 --- a/packages/@jsonql/ws/src/core/create-websocket-binding/bind-framework-to-jsonql.js +++ b/packages/@jsonql/ws/src/core/create-websocket-binding/bind-framework-to-jsonql.js @@ -2,6 +2,17 @@ import { initWebSocketClient } from './init-websocket-client' import createNsp from '../create-nsp' +/** + * This will decided how many clients we need to create based on the enableAuth + * @param {object} frameworkModule WebSocket + * @param {object} opts configuration + * @return {promise} resolve the opts with the newly created clients + */ +function createNspClients(frameworkModule, opts) { + +} + + /** * Create the framework <---> jsonql client binding * @param {object} frameworkModule the different WebSocket module @@ -21,6 +32,9 @@ function bindFrameworkToJsonql(frameworkModule) { * @return {object} passing the same 3 input out with additional in the opts */ return function createClientBindingAction(opts, nspMap, ee) { + + + opts.nspClient = nspClient opts.nspAuthClient = nspAuthClient // @1.0.7 remove later once everything fixed diff --git a/packages/@jsonql/ws/src/core/create-websocket-binding/init-websocket-client.js b/packages/@jsonql/ws/src/core/create-websocket-binding/init-websocket-client.js index 47196d861527a4bb830e1ecb7c6cb257c1b22a11..182e8aea701c694e50996bc7ab47e552d5079f4e 100644 --- a/packages/@jsonql/ws/src/core/create-websocket-binding/init-websocket-client.js +++ b/packages/@jsonql/ws/src/core/create-websocket-binding/init-websocket-client.js @@ -2,6 +2,45 @@ // this is where the framework specific code get injected import { TOKEN_PARAM_NAME } from 'jsonql-constants' import { fixWss } from '../modules' +import { + createInitPing, + extractPingResult +} from './modules' + +/** + * Group the ping and get respond create new client in one + * @param {object} ws + * @param {object} WebSocket + * @param {string} url + * @param {function} resolver + * @param {function} rejecter + * @param {boolean} auth client or not + * @return {promise} resolve the confirm client + */ +function initPingAction(ws, WebSocket, url, resolver, rejecter, auth) { + const name = auth ? 'nspAuthClient' : 'nspClient' + // @TODO how to we id this client can issue a CSRF + // by origin? + ws.onopen = function onOpenCallback() { + ws.send(createInitPing()) + } + + ws.onmessage = function onMessageCallback(payload) { + try { + const header = extractPingResult(payload.data) + // terminate the client + ws.terminate() + // return the new one with csrf header + resolver({[name]: new WebSocket(url, header)}) + } catch(e) { + rejecter(e) + } + } + + ws.onerror = function onErrorCallback(err) { + rejecter(err) + } +} /** * The bug was in the wsOptions where ws don't need it but socket.io do @@ -12,14 +51,17 @@ import { fixWss } from '../modules' */ function initWebSocketClient(WebSocket, auth = false, opts = {}) { if (auth === false) { + /** + * Create a non-protected client + * @param {string} url + * @return {promise} resolve to the confirmed client + */ return function createWsClient(url) { + return new Promise((resolver, rejecter) => { - const unconfirmClient = new WebSocket(fixWss(url)) - // @TODO how to we id this client can issue a CSRF - // by origin? - - - return + const _url = fixWss(url) + const unconfirmClient = new WebSocket(_url) + initPingAction(unconfirmClient, WebSocket, _url, resolver, rejecter, auth) }) } } @@ -33,13 +75,12 @@ function initWebSocketClient(WebSocket, auth = false, opts = {}) { return function createWsAuthClient(url, token) { const ws_url = fixWss(url) // console.log('what happen here?', url, ws_url, token) - const uri = token && typeof token === 'string' ? `${ws_url}?${TOKEN_PARAM_NAME}=${token}` : ws_url - try { - return new WebSocket(uri) - } catch(e) { - console.error('WebSocket Connection Error', e) - return false - } + const _url = token && typeof token === 'string' ? `${ws_url}?${TOKEN_PARAM_NAME}=${token}` : ws_url + + return new Promise((resolver, rejecter) => { + const unconfirmClient = new WebSocket(_url) + initPingAction(unconfirmClient, WebSocket, _url, resolver, rejecter, auth) + }) } } diff --git a/packages/@jsonql/ws/src/core/modules.js b/packages/@jsonql/ws/src/core/modules.js index bd36ddc264e2ef5ecd07665b4b74140abe9fb0db..ba826144153f52dc87d716192220e63ceb0eadec 100644 --- a/packages/@jsonql/ws/src/core/modules.js +++ b/packages/@jsonql/ws/src/core/modules.js @@ -22,7 +22,10 @@ import { EventEmitterClass, clientEventHandler, - createCombineClient + createCombineClient, + + createInitPing, + extractPingResult } from '../../../../ws-client-core/index' // 'jsonql-ws-client-core' // export @@ -48,5 +51,8 @@ export { EventEmitterClass, clientEventHandler, - createCombineClient + createCombineClient, + + createInitPing, + extractPingResult } \ No newline at end of file diff --git a/packages/constants/README.md b/packages/constants/README.md index 7b893db8a1700d747bbd488b66633ea6bb9738e9..9bf50ba6a118e6869b1fb73bcec17dbad4bd6dd0 100755 --- a/packages/constants/README.md +++ b/packages/constants/README.md @@ -221,6 +221,8 @@ Please consult the detail break down below. - PRIVATE_KEY_NAME - DEFAULT_PUBLIC_KEY_FILE - DEFAULT_PRIVATE_KEY_FILE +- NSP_AUTH_CLIENT +- NSP_CLIENT ### VALIDATION diff --git a/packages/constants/browser.js b/packages/constants/browser.js index 29d7822f901726eaf4462afbd3e2f62a40563938..b3379baa42c9f61e9c14af4bfb88379cfc979951 100644 --- a/packages/constants/browser.js +++ b/packages/constants/browser.js @@ -235,6 +235,8 @@ var jsonqlConstants = { "PRIVATE_KEY_NAME": "privateKey", "DEFAULT_PUBLIC_KEY_FILE": "publicKey.pem", "DEFAULT_PRIVATE_KEY_FILE": "privateKey.pem", + "NSP_AUTH_CLIENT": "nspAuthClient", + "NSP_CLIENT": "nspClient", "OR_SEPERATOR": "|", "FUNCTION_TYPE": "function", "STRING_TYPE": "string", diff --git a/packages/constants/constants.json b/packages/constants/constants.json index 09aa83285286d2094217ec4045c15f0385a033a2..5242ceb516d038544b079e1416468d0f97c103e5 100644 --- a/packages/constants/constants.json +++ b/packages/constants/constants.json @@ -235,6 +235,8 @@ "PRIVATE_KEY_NAME": "privateKey", "DEFAULT_PUBLIC_KEY_FILE": "publicKey.pem", "DEFAULT_PRIVATE_KEY_FILE": "privateKey.pem", + "NSP_AUTH_CLIENT": "nspAuthClient", + "NSP_CLIENT": "nspClient", "OR_SEPERATOR": "|", "FUNCTION_TYPE": "function", "STRING_TYPE": "string", diff --git a/packages/constants/index.js b/packages/constants/index.js index c701806179d3b80f194a1ef67afad32c0d046f83..15237c0f4f23e1e1ed11ddf1593d46f2b7538c97 100644 --- a/packages/constants/index.js +++ b/packages/constants/index.js @@ -345,6 +345,9 @@ export const PRIVATE_KEY_NAME = 'privateKey' export const DEFAULT_PUBLIC_KEY_FILE = [PUBLIC_KEY_NAME, PEM_EXT].join('.') export const DEFAULT_PRIVATE_KEY_FILE = [PRIVATE_KEY_NAME, PEM_EXT].join('.') + +export const NSP_AUTH_CLIENT = 'nspAuthClient' +export const NSP_CLIENT = 'nspClient' /* validation.js */ // validation related constants diff --git a/packages/constants/main.js b/packages/constants/main.js index 41ef9d0637db80c29e5c4f9625a070f572e1d205..932f0f15d0768d75d6c7ce830fdbbaaddc0d1697 100644 --- a/packages/constants/main.js +++ b/packages/constants/main.js @@ -235,6 +235,8 @@ module.exports = { "PRIVATE_KEY_NAME": "privateKey", "DEFAULT_PUBLIC_KEY_FILE": "publicKey.pem", "DEFAULT_PRIVATE_KEY_FILE": "privateKey.pem", + "NSP_AUTH_CLIENT": "nspAuthClient", + "NSP_CLIENT": "nspClient", "OR_SEPERATOR": "|", "FUNCTION_TYPE": "function", "STRING_TYPE": "string", diff --git a/packages/constants/package.json b/packages/constants/package.json index f8791cd29a8e6b058c9628366a3e2f7511268a3f..c6bb78a15ae14c8f898b0b26794676208c9f3079 100755 --- a/packages/constants/package.json +++ b/packages/constants/package.json @@ -1,6 +1,6 @@ { "name": "jsonql-constants", - "version": "2.0.3", + "version": "2.0.4", "description": "All the share constants for jsonql modules", "main": "main.js", "module": "index.js", diff --git a/packages/constants/socket.js b/packages/constants/socket.js index f151d6bded1222c1040e81881f04622fa8cff257..b346ef53e7fcd756895c777e54f449cf307c1563 100644 --- a/packages/constants/socket.js +++ b/packages/constants/socket.js @@ -108,3 +108,6 @@ export const PRIVATE_KEY_NAME = 'privateKey' export const DEFAULT_PUBLIC_KEY_FILE = [PUBLIC_KEY_NAME, PEM_EXT].join('.') export const DEFAULT_PRIVATE_KEY_FILE = [PRIVATE_KEY_NAME, PEM_EXT].join('.') + +export const NSP_AUTH_CLIENT = 'nspAuthClient' +export const NSP_CLIENT = 'nspClient' diff --git a/packages/ws-client-core/index.js b/packages/ws-client-core/index.js index 2398a68f7b8a83ffb4556d95fda0a41187dd8a54..0debf854184a62d28c4f9b188ba9d41b760b05b4 100644 --- a/packages/ws-client-core/index.js +++ b/packages/ws-client-core/index.js @@ -40,7 +40,11 @@ import { import { createCombineClient } from './src/create-combine-client' - +// new ping methods +import { + createInitPing, + extractPingResult +} from './src/share/init-ping-methods' // export export { @@ -67,5 +71,8 @@ export { EventEmitterClass, clientEventHandler, - createCombineClient + createCombineClient, + + createInitPing, + extractPingResult } diff --git a/packages/ws-client-core/package.json b/packages/ws-client-core/package.json index 07db29d37834c86a83b2fc6296e88ec8499c3b75..f2ccfadec174c9c09d28b37d4bdffe719651a045 100644 --- a/packages/ws-client-core/package.json +++ b/packages/ws-client-core/package.json @@ -1,6 +1,6 @@ { "name": "jsonql-ws-client-core", - "version": "0.9.0", + "version": "0.9.1", "description": "This is the jsonql Web Socket client core library for Node and Browser. Not for direct use.", "main": "main.js", "module": "index.js", @@ -15,6 +15,7 @@ "update:version": "node ./build.js", "test:tbd": "DEBUG=jsonql-ws* ava ./tests/tbd.test.js", "test:evt": "ava ./tests/event.test.js", + "test:init": "DEBUG=jsonql-ws* ava ./tests/init.test.js", "test:auth": "DEBUG=jsonql-ws* ava ./tests/auth.test.js", "test:node": "DEBUG=jsonql-ws-* ava ./tests/test-node.test.js", "test:opt": "DEBUG=jsonql-ws-* ava ./tests/options.test.js", @@ -54,7 +55,7 @@ }, "dependencies": { "@to1source/event": "^1.0.0", - "jsonql-constants": "^2.0.2", + "jsonql-constants": "^2.0.3", "jsonql-errors": "^1.2.1", "jsonql-params-validator": "^1.6.1", "jsonql-utils": "^1.2.0" @@ -64,7 +65,7 @@ "esm": "^3.2.25", "fs-extra": "^8.1.0", "jsonql-contract": "^1.8.10", - "jsonql-ws-server": "^1.7.3", + "jsonql-ws-server": "^1.7.4", "kefir": "^3.8.6", "ws": "^7.2.3" }, diff --git a/packages/ws-client-core/src/share/init-ping-methods.js b/packages/ws-client-core/src/share/init-ping-methods.js index bac364a94b65681c91abbe67649f20ad7a4bf366..f2c433da646c75c6de671052fe9737be80f0e9c8 100644 --- a/packages/ws-client-core/src/share/init-ping-methods.js +++ b/packages/ws-client-core/src/share/init-ping-methods.js @@ -6,20 +6,27 @@ // we use this token to create a new client and destroy the old one import { INTERCOM_RESOLVER_NAME, - SOCKET_PING_EVENT_NAME -} from 'jsonql-constants/socket' + SOCKET_PING_EVENT_NAME, + HEADERS_KEY, + DATA_KEY, + CSRF_HEADER_KEY +} from 'jsonql-constants' import { createQueryStr, extractWsPayload, timestamp, - toJson + toJson } from 'jsonql-utils/module' +import { + JsonqlError +} from 'jsonql-errors' +const CSRF_HEADER_NOT_EXIST_ERR = 'CSRF header is not in the received payload' /** * call the server to get a csrf token * @return {string} formatted payload to send to the server */ -function initPingAction() { +function createInitPing() { const ts = timestamp() return createQueryStr(INTERCOM_RESOLVER_NAME, [SOCKET_PING_EVENT_NAME, ts]) } @@ -32,9 +39,13 @@ function initPingAction() { function extractPingResult(payload) { const json = toJson(payload) const result = extractWsPayload(json) - - return {[HEADERS_KEY]: result[DATA_KEY]} + if (result[DATA_KEY] && result[DATA_KEY][CSRF_HEADER_KEY]) { + return { + [HEADERS_KEY]: result[DATA_KEY] + } + } + throw new JsonqlError('extractPingResult', CSRF_HEADER_NOT_EXIST_ERR) } -export { initPingAction, extractPingResult } \ No newline at end of file +export { createInitPing, extractPingResult } \ No newline at end of file diff --git a/packages/ws-client-core/tests/init.test.js b/packages/ws-client-core/tests/init.test.js new file mode 100644 index 0000000000000000000000000000000000000000..35e66121b4a271c8101a1cc77fe2ba3bdb546fa3 --- /dev/null +++ b/packages/ws-client-core/tests/init.test.js @@ -0,0 +1,57 @@ +// testing the init connection +const test = require('ava') +const wsNodeClient = require('jsonql-ws-server/client') +const wsServer = require('./fixtures/server-setup') +const { + JSONQL_PATH +} = require('jsonql-constants') +const debug = require('debug')('jsonql-ws-server:test:ws') +const port = 8899 + +const { createInitPing, extractPingResult } = require('../src/share/init-ping-methods') + +test.before(async t => { + const { app } = await wsServer() + + t.context.server = app + t.context.server.listen(port) + + t.context.client = wsNodeClient(`ws://localhost:${port}/${JSONQL_PATH}`) +}) + +test.after(t => { + t.context.server.close() +}) + +test.cb(`Testing the init ping call`, t => { + t.plan(1) + let client = t.context.client + + client.on('open', () => { + const payload = createInitPing() + + debug(payload) + + client.send(payload) + }) + + client.on('message', data => { + + debug('raw data', typeof data, data) + const header = extractPingResult(data) + + t.truthy(header) + + client.terminate() + + debug('header', header) + // t.end() + // reinit the client with an options + const newClient = wsNodeClient(`ws://localhost:${port}/${JSONQL_PATH}`, false, header) + + newClient.on('open', () => { + t.end() + }) + }) + +}) \ No newline at end of file diff --git a/packages/ws-server/client.js b/packages/ws-server/client.js index 15490cbe9771842c0fc2757a0ccb708576d32459..089c1b01316695688f4f2d1f356a579c868a48ec 100644 --- a/packages/ws-server/client.js +++ b/packages/ws-server/client.js @@ -10,6 +10,6 @@ const { TOKEN_PARAM_NAME } = require('jsonql-constants') * @return {object} ws instance */ module.exports = function client(url, token = false, opts = {}) { - let uri = token ? `${url}?${TOKEN_PARAM_NAME}=${token}` : url; + let uri = token ? `${url}?${TOKEN_PARAM_NAME}=${token}` : url return new WebSocket(uri, opts) } diff --git a/packages/ws-server/package.json b/packages/ws-server/package.json index fb1d0e1085aeedd8f2e426cc754cc72d71cecf7b..4ef0fa9d97bfb8bbdcafa01774a6e7f168cddcb4 100755 --- a/packages/ws-server/package.json +++ b/packages/ws-server/package.json @@ -1,10 +1,11 @@ { "name": "jsonql-ws-server", - "version": "1.7.3", + "version": "1.7.4", "description": "Setup WebSocket server for the jsonql to run on the same host, automatic generate public / private channel using contract", "main": "index.js", "files": [ "index.js", + "client.js", "src" ], "scripts": { @@ -32,7 +33,7 @@ "dependencies": { "colors": "^1.4.0", "debug": "^4.1.1", - "jsonql-constants": "^2.0.2", + "jsonql-constants": "^2.0.3", "jsonql-utils": "^1.2.0", "jsonql-ws-server-core": "^0.7.6", "ws": "^7.2.3"