diff --git a/.prettierignore b/.prettierignore index 8f567aa4..aa387a2f 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,2 @@ test/support/public/engine.io.min.js +lib/contrib/* diff --git a/lib/contrib/has-cors.ts b/lib/contrib/has-cors.ts new file mode 100644 index 00000000..d993c593 --- /dev/null +++ b/lib/contrib/has-cors.ts @@ -0,0 +1,12 @@ +// imported from https://github.com/component/has-cors +let value = false; + +try { + value = typeof XMLHttpRequest !== 'undefined' && + 'withCredentials' in new XMLHttpRequest(); +} catch (err) { + // if XMLHttp support is disabled in IE then it will throw + // when trying to create +} + +export const hasCORS = value; diff --git a/lib/contrib/parseqs.ts b/lib/contrib/parseqs.ts new file mode 100644 index 00000000..e7650766 --- /dev/null +++ b/lib/contrib/parseqs.ts @@ -0,0 +1,38 @@ +// imported from https://github.com/galkn/querystring +/** + * Compiles a querystring + * Returns string representation of the object + * + * @param {Object} + * @api private + */ + +export function encode (obj) { + let str = ''; + + for (let i in obj) { + if (obj.hasOwnProperty(i)) { + if (str.length) str += '&'; + str += encodeURIComponent(i) + '=' + encodeURIComponent(obj[i]); + } + } + + return str; +} + +/** + * Parses a simple querystring into an object + * + * @param {String} qs + * @api private + */ + +export function decode (qs) { + let qry = {}; + let pairs = qs.split('&'); + for (let i = 0, l = pairs.length; i < l; i++) { + let pair = pairs[i].split('='); + qry[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]); + } + return qry; +} diff --git a/lib/contrib/parseuri.ts b/lib/contrib/parseuri.ts new file mode 100644 index 00000000..48e05135 --- /dev/null +++ b/lib/contrib/parseuri.ts @@ -0,0 +1,68 @@ +// imported from https://github.com/galkn/parseuri +/** + * Parses an URI + * + * @author Steven Levithan (MIT license) + * @api private + */ +const re = /^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/; + +const parts = [ + 'source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor' +]; + +export function parse(str) { + const src = str, + b = str.indexOf('['), + e = str.indexOf(']'); + + if (b != -1 && e != -1) { + str = str.substring(0, b) + str.substring(b, e).replace(/:/g, ';') + str.substring(e, str.length); + } + + let m = re.exec(str || ''), + uri = {} as any, + i = 14; + + while (i--) { + uri[parts[i]] = m[i] || ''; + } + + if (b != -1 && e != -1) { + uri.source = src; + uri.host = uri.host.substring(1, uri.host.length - 1).replace(/;/g, ':'); + uri.authority = uri.authority.replace('[', '').replace(']', '').replace(/;/g, ':'); + uri.ipv6uri = true; + } + + uri.pathNames = pathNames(uri, uri['path']); + uri.queryKey = queryKey(uri, uri['query']); + + return uri; +} + +function pathNames(obj, path) { + const regx = /\/{2,9}/g, + names = path.replace(regx, "/").split("/"); + + if (path.substr(0, 1) == '/' || path.length === 0) { + names.splice(0, 1); + } + if (path.substr(path.length - 1, 1) == '/') { + names.splice(names.length - 1, 1); + } + + return names; +} + +function queryKey(uri, query) { + const data = {}; + + query.replace(/(?:^|&)([^&=]*)=?([^&]*)/g, function ($0, $1, $2) { + if ($1) { + data[$1] = $2; + } + }); + + return data; +} diff --git a/lib/contrib/yeast.ts b/lib/contrib/yeast.ts new file mode 100644 index 00000000..71ad33ea --- /dev/null +++ b/lib/contrib/yeast.ts @@ -0,0 +1,62 @@ +// imported from https://github.com/unshiftio/yeast +'use strict'; + +const alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_'.split('') + , length = 64 + , map = {}; +let seed = 0 + , i = 0 + , prev; + +/** + * Return a string representing the specified number. + * + * @param {Number} num The number to convert. + * @returns {String} The string representation of the number. + * @api public + */ +export function encode(num) { + let encoded = ''; + + do { + encoded = alphabet[num % length] + encoded; + num = Math.floor(num / length); + } while (num > 0); + + return encoded; +} + +/** + * Return the integer value specified by the given string. + * + * @param {String} str The string to convert. + * @returns {Number} The integer value represented by the string. + * @api public + */ +export function decode(str) { + let decoded = 0; + + for (i = 0; i < str.length; i++) { + decoded = decoded * length + map[str.charAt(i)]; + } + + return decoded; +} + +/** + * Yeast: A tiny growing id generator. + * + * @returns {String} A unique id. + * @api public + */ +export function yeast() { + const now = encode(+new Date()); + + if (now !== prev) return seed = 0, prev = now; + return now +'.'+ encode(seed++); +} + +// +// Map each character to its index. +// +for (; i < length; i++) map[alphabet[i]] = i; diff --git a/lib/index.ts b/lib/index.ts index 625ebfd5..ae593d2d 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -6,3 +6,4 @@ export const protocol = Socket.protocol; export { Transport } from "./transport.js"; export { transports } from "./transports/index.js"; export { installTimerFunctions } from "./util.js"; +export { parse } from "./contrib/parseuri"; diff --git a/lib/socket.ts b/lib/socket.ts index 22ae36fa..2064579c 100644 --- a/lib/socket.ts +++ b/lib/socket.ts @@ -1,7 +1,7 @@ import { transports } from "./transports/index.js"; import { installTimerFunctions, byteLength } from "./util.js"; -import parseqs from "parseqs"; -import parseuri from "parseuri"; +import { decode } from "./contrib/parseqs.js"; +import { parse } from "./contrib/parseuri.js"; import debugModule from "debug"; // debug() import { Emitter } from "@socket.io/component-emitter"; import { protocol } from "engine.io-parser"; @@ -277,13 +277,13 @@ export class Socket extends Emitter<{}, {}, SocketReservedEvents> { } if (uri) { - uri = parseuri(uri); + uri = parse(uri); opts.hostname = uri.host; opts.secure = uri.protocol === "https" || uri.protocol === "wss"; opts.port = uri.port; if (uri.query) opts.query = uri.query; } else if (opts.host) { - opts.hostname = parseuri(opts.host).host; + opts.hostname = parse(opts.host).host; } installTimerFunctions(this, opts); @@ -335,7 +335,7 @@ export class Socket extends Emitter<{}, {}, SocketReservedEvents> { this.opts.path = this.opts.path.replace(/\/$/, "") + "/"; if (typeof this.opts.query === "string") { - this.opts.query = parseqs.decode(this.opts.query); + this.opts.query = decode(this.opts.query); } // set on handshake diff --git a/lib/transports/polling.ts b/lib/transports/polling.ts index 33ef05cd..78f5e28c 100644 --- a/lib/transports/polling.ts +++ b/lib/transports/polling.ts @@ -1,7 +1,7 @@ import { Transport } from "../transport.js"; import debugModule from "debug"; // debug() -import yeast from "yeast"; -import parseqs from "parseqs"; +import { yeast } from "../contrib/yeast.js"; +import { encode } from "../contrib/parseqs.js"; import { encodePayload, decodePayload, RawData } from "engine.io-parser"; import XMLHttpRequest from "./xmlhttprequest.js"; import { Emitter } from "@socket.io/component-emitter"; @@ -235,7 +235,7 @@ export class Polling extends Transport { port = ":" + this.opts.port; } - const encodedQuery = parseqs.encode(query); + const encodedQuery = encode(query); const ipv6 = this.opts.hostname.indexOf(":") !== -1; return ( diff --git a/lib/transports/websocket.ts b/lib/transports/websocket.ts index a85cca86..853c2fed 100644 --- a/lib/transports/websocket.ts +++ b/lib/transports/websocket.ts @@ -1,6 +1,6 @@ import { Transport } from "../transport.js"; -import parseqs from "parseqs"; -import yeast from "yeast"; +import { encode } from "../contrib/parseqs.js"; +import { yeast } from "../contrib/yeast.js"; import { pick } from "../util.js"; import { defaultBinaryType, @@ -220,7 +220,7 @@ export class WS extends Transport { query.b64 = 1; } - const encodedQuery = parseqs.encode(query); + const encodedQuery = encode(query); const ipv6 = this.opts.hostname.indexOf(":") !== -1; return ( diff --git a/lib/transports/xmlhttprequest.browser.ts b/lib/transports/xmlhttprequest.browser.ts index 6da802c7..ec71b40a 100644 --- a/lib/transports/xmlhttprequest.browser.ts +++ b/lib/transports/xmlhttprequest.browser.ts @@ -1,6 +1,6 @@ // browser shim for xmlhttprequest module -import hasCORS from "has-cors"; +import { hasCORS } from "../contrib/has-cors.js"; import globalThis from "../globalThis.js"; export default function(opts) { diff --git a/lib/xmlhttprequest.ts b/lib/xmlhttprequest.ts index a9c5d1e1..660d4135 100644 --- a/lib/xmlhttprequest.ts +++ b/lib/xmlhttprequest.ts @@ -1,6 +1,6 @@ // browser shim for xmlhttprequest module -import hasCORS from "has-cors"; +import { hasCORS } from "./contrib/has-cors.js"; import globalThis from "./globalThis.js"; export default function(opts) { diff --git a/package-lock.json b/package-lock.json index f8ca23ab..3bee70f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,12 +12,8 @@ "@socket.io/component-emitter": "~3.0.0", "debug": "~4.3.1", "engine.io-parser": "~5.0.0", - "has-cors": "1.1.0", - "parseqs": "0.0.6", - "parseuri": "0.0.6", "ws": "~8.2.3", - "xmlhttprequest-ssl": "~2.0.0", - "yeast": "0.1.2" + "xmlhttprequest-ssl": "~2.0.0" }, "devDependencies": { "@babel/core": "^7.12.9", @@ -6541,11 +6537,6 @@ "node": ">= 0.4.0" } }, - "node_modules/has-cors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" - }, "node_modules/has-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", @@ -9482,16 +9473,6 @@ "node": ">=0.10.0" } }, - "node_modules/parseqs": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", - "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==" - }, - "node_modules/parseuri": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", - "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==" - }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -13013,11 +12994,6 @@ "node": ">=4" } }, - "node_modules/yeast": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", - "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" - }, "node_modules/zip-stream": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-0.2.3.tgz", @@ -18799,11 +18775,6 @@ "function-bind": "^1.1.1" } }, - "has-cors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" - }, "has-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", @@ -21223,16 +21194,6 @@ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", "dev": true }, - "parseqs": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", - "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==" - }, - "parseuri": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", - "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==" - }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -24129,11 +24090,6 @@ "decamelize": "^1.2.0" } }, - "yeast": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", - "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" - }, "zip-stream": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-0.2.3.tgz", diff --git a/package.json b/package.json index 9888a7ab..fac4ba71 100644 --- a/package.json +++ b/package.json @@ -42,12 +42,8 @@ "@socket.io/component-emitter": "~3.0.0", "debug": "~4.3.1", "engine.io-parser": "~5.0.0", - "has-cors": "1.1.0", - "parseqs": "0.0.6", - "parseuri": "0.0.6", "ws": "~8.2.3", - "xmlhttprequest-ssl": "~2.0.0", - "yeast": "0.1.2" + "xmlhttprequest-ssl": "~2.0.0" }, "devDependencies": { "@babel/core": "^7.12.9",