diff --git a/lib/index.d.ts b/lib/index.d.ts index 9ee624f..c5d60a4 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -20,3 +20,12 @@ export interface Options { export function request(url: string, options: Options): Promise; export function read(response: IncomingMessage, encoding: string): Promise; + +export interface Event { + 'data'?: string; + 'id'?: string; + 'event'?: string; + 'retry'?: string; +} + +export function readAsSSE(response: IncomingMessage): AsyncGenerator; diff --git a/lib/index.js b/lib/index.js index 81500e6..677d01d 100644 --- a/lib/index.js +++ b/lib/index.js @@ -18,6 +18,23 @@ const READ_TIMER = Symbol('TIMER::READ_TIMER'); const READ_TIME_OUT = Symbol('TIMER::READ_TIME_OUT'); const READ_TIMER_START_AT = Symbol('TIMER::READ_TIMER_START_AT'); +/** + * Check the content-encoding header, and auto decompress it. + * @param {Readable} response http response + * @returns Readable + */ +function decompress(response) { + switch (response.headers['content-encoding']) { + // or, just use zlib.createUnzip() to handle both cases + case 'gzip': + return response.pipe(zlib.createGunzip()); + case 'deflate': + return response.pipe(zlib.createInflate()); + default: + return response; + } +} + var append = function (err, name, message) { err.name = name + err.name; err.message = `${message}. ${err.message}`; @@ -29,7 +46,6 @@ const isNumber = function (num) { }; exports.request = function (url, opts) { - // request(url) opts || (opts = {}); const parsed = typeof url === 'string' ? parse(url) : url; @@ -165,18 +181,7 @@ exports.request = function (url, opts) { }; exports.read = function (response, encoding) { - var readable = response; - switch (response.headers['content-encoding']) { - // or, just use zlib.createUnzip() to handle both cases - case 'gzip': - readable = response.pipe(zlib.createGunzip()); - break; - case 'deflate': - readable = response.pipe(zlib.createInflate()); - break; - default: - break; - } + const readable = decompress(response); return new Promise((resolve, reject) => { // node.js 14 use response.client @@ -254,3 +259,113 @@ exports.read = function (response, encoding) { readable.on('end', onEnd); }); }; + +function readyToRead(readable) { + return new Promise((resolve, reject) => { + var onReadable, onEnd, onError; + var cleanup = function () { + // cleanup + readable.removeListener('error', onError); + readable.removeListener('end', onEnd); + readable.removeListener('readable', onReadable); + } + + onReadable = function () { + cleanup(); + resolve(false); + }; + + onEnd = function () { + cleanup(); + resolve(true); + } + + onError = function (err) { + cleanup(); + reject(err); + } + + readable.once('readable', onReadable); + readable.once('end', onEnd); + readable.once('error', onError); + }); +} + +class Event { + constructor(id, event, data, retry) { + this.id = id; + this.event = event; + this.data = data; + this.retry = retry; + } +} + +exports.Event = Event; + +const DATA_PREFIX = 'data: '; +const EVENT_PREFIX = 'event: '; +const ID_PREFIX = 'id: '; +const RETRY_PREFIX = 'retry: '; + +function tryGetEvents(head, chunk) { + const all = head + chunk; + let start = 0; + const events = []; + for (let i = 0; i < all.length - 1; i++) { + const c = all[i]; + const c2 = all[i + 1]; + if (c === '\n' && c2 === '\n') { + const part = all.substring(start, i); + const lines = part.split('\n'); + const event = new Event(); + lines.forEach((line) => { + if (line.startsWith(DATA_PREFIX)) { + event.data = line.substring(DATA_PREFIX.length); + } else if (line.startsWith(EVENT_PREFIX)) { + event.event = line.substring(EVENT_PREFIX.length); + } else if (line.startsWith(ID_PREFIX)) { + event.id = line.substring(ID_PREFIX.length); + } else if (line.startsWith(RETRY_PREFIX)) { + event.retry = line.substring(RETRY_PREFIX.length); + } + }); + events.push(event); + start = i + 2; + } + } + + const rest = all.substring(start); + return [events, rest]; +} + +/** + * consume response and parse to event stream + * @param {ReadableStream} response + * @returns AsyncGenerator + */ +exports.readAsSSE = async function* (response) { + const readable = decompress(response); + + const socket = response.socket || response.client; + clearTimeout(socket[READ_TIMER]); + + let rest = ''; + + while (true) { + const ended = await readyToRead(readable); + if (ended) { + return; + } + + let chunk; + while (null !== (chunk = readable.read())) { + const [ events, remain ] = tryGetEvents(rest, chunk.toString()); + rest = remain; + if (events && events.length > 0) { + for (const event of events) { + yield event; + } + } + } + } +}; diff --git a/package-lock.json b/package-lock.json index bcac915..49895d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,14 +10,14 @@ "license": "MIT", "dependencies": { "@types/node": "^20", - "debug": "^4.1.1", - "socksv5": "^0.0.6" + "debug": "^4.1.1" }, "devDependencies": { "eslint": "^8.26.0", "mocha": "^10", "nyc": "^15.1.0", - "socks-proxy-agent": "^8" + "socks-proxy-agent": "^7", + "socksv5": "^0.0.6" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -622,15 +622,15 @@ } }, "node_modules/agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, "dependencies": { - "debug": "^4.3.4" + "debug": "4" }, "engines": { - "node": ">= 14" + "node": ">= 6.0.0" } }, "node_modules/aggregate-error": { @@ -732,6 +732,12 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==", + "dev": true + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -894,6 +900,32 @@ "node": ">=6" } }, + "node_modules/cli": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz", + "integrity": "sha512-dbn5HyeJWSOU58RwOEiF1VWrl7HRvDsKLpu0uiI/vExH6iNoyUzjB5Mr3IJY5DVUfnbpe9793xw4DFJVzC9nWQ==", + "dev": true, + "dependencies": { + "glob": ">= 3.1.4" + }, + "engines": { + "node": ">=0.2.5" + } + }, + "node_modules/cliff": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/cliff/-/cliff-0.1.10.tgz", + "integrity": "sha512-roZWcC2Cxo/kKjRXw7YUpVNtxJccbvcl7VzTjUYgLQk6Ot0R8bm2netbhSZYWWNrKlOO/7HD6GXHl8dtzE6SiQ==", + "dev": true, + "dependencies": { + "colors": "~1.0.3", + "eyes": "~0.1.8", + "winston": "0.8.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -926,6 +958,15 @@ "integrity": "sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w==", "dev": true }, + "node_modules/colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -967,6 +1008,15 @@ "node": ">= 8" } }, + "node_modules/cycle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", + "integrity": "sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -1336,6 +1386,15 @@ "node": ">=0.10.0" } }, + "node_modules/eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==", + "dev": true, + "engines": { + "node": "> 0.1.90" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1818,6 +1877,12 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "dev": true + }, "node_modules/istanbul-lib-coverage": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", @@ -2748,6 +2813,15 @@ "node": ">=8" } }, + "node_modules/pkginfo": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", + "integrity": "sha512-yO5feByMzAp96LtP58wvPKSbaKAi/1C4kV9XpTctr6EepnP6F33RBNOiVrdz9BrPA98U2BMFsTNHo44TWcbQ2A==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -2999,17 +3073,17 @@ } }, "node_modules/socks-proxy-agent": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz", - "integrity": "sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", "dev": true, "dependencies": { - "agent-base": "^7.0.2", - "debug": "^4.3.4", - "socks": "^2.7.1" + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" }, "engines": { - "node": ">= 14" + "node": ">= 10" } }, "node_modules/socksv5": { @@ -3019,6 +3093,7 @@ "bundleDependencies": [ "ipv6" ], + "dev": true, "dependencies": { "ipv6": "*" }, @@ -3082,6 +3157,15 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/string-width": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", @@ -3252,6 +3336,33 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "node_modules/winston": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-0.8.3.tgz", + "integrity": "sha512-fPoamsHq8leJ62D1M9V/f15mjQ1UHe4+7j1wpAT3fqgA5JqhJkk4aIfPEjfMTI9x6ZTjaLOpMAjluLtmgO5b6g==", + "dev": true, + "dependencies": { + "async": "0.2.x", + "colors": "0.6.x", + "cycle": "1.0.x", + "eyes": "0.1.x", + "isstream": "0.1.x", + "pkginfo": "0.3.x", + "stack-trace": "0.0.x" + }, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/winston/node_modules/colors": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", + "integrity": "sha512-OsSVtHK8Ir8r3+Fxw/b4jS1ZLPXkV6ZxDRJQzeD7qo0SqMXWrHDM71DgYzPMHY8SFJ0Ao+nNU2p1MmwdzKqPrw==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/workerpool": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", @@ -3839,12 +3950,12 @@ "requires": {} }, "agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, "requires": { - "debug": "^4.3.4" + "debug": "4" } }, "aggregate-error": { @@ -3924,6 +4035,12 @@ "sprintf-js": "~1.0.2" } }, + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==", + "dev": true + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -4037,6 +4154,26 @@ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true }, + "cli": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz", + "integrity": "sha512-dbn5HyeJWSOU58RwOEiF1VWrl7HRvDsKLpu0uiI/vExH6iNoyUzjB5Mr3IJY5DVUfnbpe9793xw4DFJVzC9nWQ==", + "dev": true, + "requires": { + "glob": ">= 3.1.4" + } + }, + "cliff": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/cliff/-/cliff-0.1.10.tgz", + "integrity": "sha512-roZWcC2Cxo/kKjRXw7YUpVNtxJccbvcl7VzTjUYgLQk6Ot0R8bm2netbhSZYWWNrKlOO/7HD6GXHl8dtzE6SiQ==", + "dev": true, + "requires": { + "colors": "~1.0.3", + "eyes": "~0.1.8", + "winston": "0.8.x" + } + }, "cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -4069,6 +4206,12 @@ "integrity": "sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w==", "dev": true }, + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", + "dev": true + }, "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -4109,6 +4252,12 @@ "which": "^2.0.1" } }, + "cycle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", + "integrity": "sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==", + "dev": true + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -4372,6 +4521,12 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==", + "dev": true + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -4715,6 +4870,12 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "dev": true + }, "istanbul-lib-coverage": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", @@ -5415,6 +5576,12 @@ } } }, + "pkginfo": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", + "integrity": "sha512-yO5feByMzAp96LtP58wvPKSbaKAi/1C4kV9XpTctr6EepnP6F33RBNOiVrdz9BrPA98U2BMFsTNHo44TWcbQ2A==", + "dev": true + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -5576,20 +5743,21 @@ } }, "socks-proxy-agent": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz", - "integrity": "sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", "dev": true, "requires": { - "agent-base": "^7.0.2", - "debug": "^4.3.4", - "socks": "^2.7.1" + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" } }, "socksv5": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/socksv5/-/socksv5-0.0.6.tgz", "integrity": "sha512-tQpQ0MdNQAsQBDhCXy3OvGGJikh9QOl3PkbwT4POJiQCm/fK4z9AxKQQRG8WLeF6talphnPrSWiZRpTl42rApg==", + "dev": true, "requires": { "ipv6": "*" }, @@ -5637,6 +5805,12 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "dev": true + }, "string-width": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", @@ -5764,6 +5938,29 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "winston": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-0.8.3.tgz", + "integrity": "sha512-fPoamsHq8leJ62D1M9V/f15mjQ1UHe4+7j1wpAT3fqgA5JqhJkk4aIfPEjfMTI9x6ZTjaLOpMAjluLtmgO5b6g==", + "dev": true, + "requires": { + "async": "0.2.x", + "colors": "0.6.x", + "cycle": "1.0.x", + "eyes": "0.1.x", + "isstream": "0.1.x", + "pkginfo": "0.3.x", + "stack-trace": "0.0.x" + }, + "dependencies": { + "colors": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", + "integrity": "sha512-OsSVtHK8Ir8r3+Fxw/b4jS1ZLPXkV6ZxDRJQzeD7qo0SqMXWrHDM71DgYzPMHY8SFJ0Ao+nNU2p1MmwdzKqPrw==", + "dev": true + } + } + }, "workerpool": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", diff --git a/package.json b/package.json index 28f0cb1..3c9dc9d 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "lib/index.js", "types": "lib/index.d.ts", "scripts": { - "lint": "eslint --fix lib", + "lint": "eslint --fix lib test", "test": "mocha --reporter spec --timeout 3000 test/*.test.js", "test-cov": "nyc -r=lcov -r=html -r=text -r=json mocha -t 3000 -R spec test/*.test.js" }, diff --git a/test/index.test.js b/test/index.test.js index c7ae0f9..c51735f 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -10,7 +10,7 @@ const { SocksProxyAgent } = require('socks-proxy-agent'); const httpx = require('../'); const server = http.createServer((req, res) => { - if(req.url === '/readTimeout') { + if (req.url === '/readTimeout') { setTimeout(() => { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello world!'); @@ -37,6 +37,24 @@ const server = http.createServer((req, res) => { zlib.deflate('Hello world with deflate!', function (err, buff) { res.end(buff); }); + } else if (req.url === '/sse') { + const headers = { + 'Content-Type': 'text/event-stream', + 'Connection': 'keep-alive', + 'Cache-Control': 'no-cache' + }; + res.writeHead(200, headers); + res.flushHeaders(); + let count = 0; + let timer = setInterval(() => { + if (count >= 10) { + clearInterval(timer); + res.end(); + return; + } + res.write(`data: ${JSON.stringify({count: count})}\n\n`); + count++; + }, 100); } else { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello world!'); @@ -67,7 +85,8 @@ describe('httpx', () => { server.listen(0, done); }); - after((done) => { + after(function (done) { + this.timeout(20000); srv.close(); server.close(done); }); @@ -171,4 +190,21 @@ describe('httpx', () => { var result = await httpx.read(res, 'utf8'); assert.strictEqual(result, 'Hello world!'); }); + + it('readAsSSE should ok', async function () { + this.timeout(15000); + var res = await make(server)('/sse', {readTimeout: 5000}); + assert.strictEqual(res.statusCode, 200); + const events = []; + for await (const event of httpx.readAsSSE(res)) { + events.push(event); + } + + assert.strictEqual(events.length, 10); + const counts = events.map((event) => JSON.parse(event.data).count); + + assert.deepStrictEqual([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 + ], counts); + }); });