Last active
September 27, 2020 00:18
-
-
Save flaviut/fd89b3026826c18623a09cacd68d05b2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// generates a websocket for a sockjs endpoint, as defined in | |
// https://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html | |
// | |
// This websocket works just like a regular websocket, and is compatible with | |
// stompjs. | |
// | |
// `onmessage` and `send()` are modified to transparently handle converting to and | |
// from sockjs messages | |
// | |
// (c) 2020, Flaviu Tamas | |
// https://gist.github.com/flaviut/fd89b3026826c18623a09cacd68d05b2 | |
// SPDX-License-Identifier: MIT | |
export function sockJsFactory(url: string): WebSocket { | |
// technically we're supposed to fetch '${url}/info' to figure out if the | |
// server supports websockets. But we're just going to go ahead and assume | |
// that's the case, since we don't have a good way of handling that error. | |
const serverId = String(randomInteger(0, 1000)).padStart(3, '0'); | |
const sessionId = randomStr(12); | |
const socketUrl = new URL(url) | |
socketUrl.pathname = `${socketUrl.pathname}/${serverId}/${sessionId}/websocket` | |
if (socketUrl.protocol.startsWith('http')) { | |
socketUrl.protocol = socketUrl.protocol === 'http:' ? 'ws:' : 'wss:'; | |
} | |
const socket = new WebSocket(socketUrl.toString()) | |
const handler: ProxyHandler<WebSocket> = { | |
set: (obj, prop, value) => { | |
if (prop === 'onmessage' && value != null) { | |
obj[prop] = (ev: MessageEvent) => { | |
const data: string = ev.data; | |
// message frames start with "a", see | |
// https://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-42 | |
if (data.startsWith('a')) { | |
JSON.parse(data.substring(1)) | |
.forEach((submsg: string) => { | |
(value as ((ev: MessageEvent) => void)).call( | |
obj as any, | |
new MessageEvent(ev.type, { | |
data: submsg, | |
lastEventId: ev.lastEventId, | |
origin: ev.origin, | |
ports: ev.ports, | |
source: ev.source, | |
bubbles: ev.bubbles, | |
cancelable: ev.cancelable, | |
composed: ev.composed, | |
} as MessageEventInit) | |
) | |
}); | |
} | |
}; | |
return true; | |
} | |
(obj as any)[prop] = value; | |
return true | |
}, | |
get: (target, key) => { | |
if (key === "send") { | |
return (data: any) => target.send(JSON.stringify([data])); | |
} | |
const result = key in target ? (target as any)[key] : undefined; | |
if (typeof result === 'function') { | |
return (result as () => void).bind(target); | |
} | |
return result; | |
} | |
} | |
return new Proxy(socket, handler); | |
} | |
// from https://stackoverflow.com/a/55544949/2299084 | |
function randomInteger(min: number, max: number): number { | |
const range = max - min; | |
const maxGeneratedValue = 0xFFFFFFFF; | |
const possibleResultValues = range + 1; | |
const possibleGeneratedValues = maxGeneratedValue + 1; | |
const remainder = possibleGeneratedValues % possibleResultValues; | |
const maxUnbiased = maxGeneratedValue - remainder; | |
if (!Number.isInteger(min) || !Number.isInteger(max) || | |
max > Number.MAX_SAFE_INTEGER || min < Number.MIN_SAFE_INTEGER) { | |
throw new Error('Arguments must be safe integers.'); | |
} else if (range > maxGeneratedValue) { | |
throw new Error(`Range of ${range} (from ${min} to ${max}) > ${maxGeneratedValue}.`); | |
} else if (max < min) { | |
throw new Error(`max (${max}) must be >= min (${min}).`); | |
} else if (min === max) { | |
return min; | |
} | |
let generated; | |
do { | |
generated = crypto.getRandomValues(new Uint32Array(1))[0]; | |
} while (generated > maxUnbiased); | |
return min + (generated % possibleResultValues); | |
}; | |
const dictionary = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; | |
function randomStr(length: number = 12): string { | |
let result = ""; | |
for (let i = 0; i < length; i++) { | |
result += dictionary[randomInteger(0, dictionary.length)]; | |
} | |
return result | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment