diff --git a/.travis.yml b/.travis.yml index 40992555bf..f62cdac068 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,54 +1,33 @@ sudo: false language: node_js before_install: - - npm install -g npm@2 - - test $NPM_LEGACY && npm install -g npm@latest-3 || npm install npm -g + - (test $NPM_LEGACY && npm install -g npm@2 && npm install -g npm@3) || true notifications: email: false matrix: fast_finish: true include: - node_js: '0.8' - env: - - TASK=test - - NPM_LEGACY=true + env: NPM_LEGACY=true - node_js: '0.10' - env: - - TASK=test - - NPM_LEGACY=true + env: NPM_LEGACY=true - node_js: '0.11' - env: - - TASK=test - - NPM_LEGACY=true + env: NPM_LEGACY=true - node_js: '0.12' - env: - - TASK=test - - NPM_LEGACY=true + env: NPM_LEGACY=true - node_js: 1 - env: - - TASK=test - - NPM_LEGACY=true + env: NPM_LEGACY=true - node_js: 2 - env: - - TASK=test - - NPM_LEGACY=true + env: NPM_LEGACY=true - node_js: 3 - env: - - TASK=test - - NPM_LEGACY=true + env: NPM_LEGACY=true - node_js: 4 - env: TASK=test - node_js: 5 - env: TASK=test - node_js: 6 - env: TASK=test - node_js: 7 - env: TASK=test - node_js: 8 - env: TASK=test - node_js: 9 - env: TASK=test -script: "npm run $TASK" +script: "npm run test" env: global: - secure: rE2Vvo7vnjabYNULNyLFxOyt98BoJexDqsiOnfiD6kLYYsiQGfr/sbZkPMOFm9qfQG7pjqx+zZWZjGSswhTt+626C0t/njXqug7Yps4c3dFblzGfreQHp7wNX5TFsvrxd6dAowVasMp61sJcRnB2w8cUzoe3RAYUDHyiHktwqMc= diff --git a/README.md b/README.md index 23fe3f3e30..f1c5a9314f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # readable-stream -***Node-core v8.11.1 streams for userland*** [![Build Status](https://travis-ci.org/nodejs/readable-stream.svg?branch=master)](https://travis-ci.org/nodejs/readable-stream) +***Node-core v8.17.0 streams for userland*** [![Build Status](https://travis-ci.org/nodejs/readable-stream.svg?branch=master)](https://travis-ci.org/nodejs/readable-stream) [![NPM](https://nodei.co/npm/readable-stream.png?downloads=true&downloadRank=true)](https://nodei.co/npm/readable-stream/) @@ -18,7 +18,7 @@ npm install --save readable-stream This package is a mirror of the Streams2 and Streams3 implementations in Node-core. -Full documentation may be found on the [Node.js website](https://nodejs.org/dist/v8.11.1/docs/api/stream.html). +Full documentation may be found on the [Node.js website](https://nodejs.org/dist/v8.17.0/docs/api/stream.html). If you want to guarantee a stable streams base, regardless of what version of Node you, or the users of your libraries are using, use **readable-stream** *only* and avoid the *"stream"* module in Node-core, for background see [this blogpost](http://r.va.gg/2014/06/why-i-dont-use-nodes-core-stream-module.html). diff --git a/build/files.js b/build/files.js index 9bf53a6ee4..6a5a34299a 100644 --- a/build/files.js +++ b/build/files.js @@ -41,7 +41,7 @@ const headRegexp = /(^module.exports = \w+;?)/m , utilReplacement = [ /^const util = require\('util'\);/m - , '\n/**/\nconst util = require(\'core-util-is\');\n' + , '\n/**/\nconst util = Object.create(require(\'core-util-is\'));\n' + 'util.inherits = require(\'inherits\');\n/**/\n' ] @@ -179,7 +179,7 @@ const headRegexp = /(^module.exports = \w+;?)/m /(?:var|const) Buffer = require\('safe-buffer'\)\.Buffer;/ , ` const Buffer = require('safe-buffer').Buffer - const OurUint8Array = global.Uint8Array || function () {} + const OurUint8Array = (typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : typeof self !== 'undefined' ? self : {}).Uint8Array || function () {} function _uint8ArrayToBuffer(chunk) { return Buffer.from(chunk); } diff --git a/lib/_stream_duplex.js b/lib/_stream_duplex.js index a1ca813e5a..57003c32d2 100644 --- a/lib/_stream_duplex.js +++ b/lib/_stream_duplex.js @@ -43,7 +43,7 @@ var objectKeys = Object.keys || function (obj) { module.exports = Duplex; /**/ -var util = require('core-util-is'); +var util = Object.create(require('core-util-is')); util.inherits = require('inherits'); /**/ diff --git a/lib/_stream_passthrough.js b/lib/_stream_passthrough.js index a9c8358848..612edb4d8b 100644 --- a/lib/_stream_passthrough.js +++ b/lib/_stream_passthrough.js @@ -30,7 +30,7 @@ module.exports = PassThrough; var Transform = require('./_stream_transform'); /**/ -var util = require('core-util-is'); +var util = Object.create(require('core-util-is')); util.inherits = require('inherits'); /**/ diff --git a/lib/_stream_readable.js b/lib/_stream_readable.js index bf34ac65e1..3af95cb2db 100644 --- a/lib/_stream_readable.js +++ b/lib/_stream_readable.js @@ -53,7 +53,7 @@ var Stream = require('./internal/streams/stream'); /**/ var Buffer = require('safe-buffer').Buffer; -var OurUint8Array = global.Uint8Array || function () {}; +var OurUint8Array = (typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : typeof self !== 'undefined' ? self : {}).Uint8Array || function () {}; function _uint8ArrayToBuffer(chunk) { return Buffer.from(chunk); } @@ -64,7 +64,7 @@ function _isUint8Array(obj) { /**/ /**/ -var util = require('core-util-is'); +var util = Object.create(require('core-util-is')); util.inherits = require('inherits'); /**/ @@ -623,8 +623,8 @@ Readable.prototype.pipe = function (dest, pipeOpts) { // also returned false. // => Check whether `dest` is still a piping destination. if ((state.pipesCount === 1 && state.pipes === dest || state.pipesCount > 1 && indexOf(state.pipes, dest) !== -1) && !cleanedUp) { - debug('false write response, pause', src._readableState.awaitDrain); - src._readableState.awaitDrain++; + debug('false write response, pause', state.awaitDrain); + state.awaitDrain++; increasedAwaitDrain = true; } src.pause(); @@ -718,7 +718,7 @@ Readable.prototype.unpipe = function (dest) { state.flowing = false; for (var i = 0; i < len; i++) { - dests[i].emit('unpipe', this, unpipeInfo); + dests[i].emit('unpipe', this, { hasUnpiped: false }); }return this; } diff --git a/lib/_stream_transform.js b/lib/_stream_transform.js index 5d1f8b876d..fcfc105af8 100644 --- a/lib/_stream_transform.js +++ b/lib/_stream_transform.js @@ -68,7 +68,7 @@ module.exports = Transform; var Duplex = require('./_stream_duplex'); /**/ -var util = require('core-util-is'); +var util = Object.create(require('core-util-is')); util.inherits = require('inherits'); /**/ diff --git a/lib/_stream_writable.js b/lib/_stream_writable.js index b3f4e85a2f..e1e897ff3b 100644 --- a/lib/_stream_writable.js +++ b/lib/_stream_writable.js @@ -64,7 +64,7 @@ var Duplex; Writable.WritableState = WritableState; /**/ -var util = require('core-util-is'); +var util = Object.create(require('core-util-is')); util.inherits = require('inherits'); /**/ @@ -81,7 +81,7 @@ var Stream = require('./internal/streams/stream'); /**/ var Buffer = require('safe-buffer').Buffer; -var OurUint8Array = global.Uint8Array || function () {}; +var OurUint8Array = (typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : typeof self !== 'undefined' ? self : {}).Uint8Array || function () {}; function _uint8ArrayToBuffer(chunk) { return Buffer.from(chunk); } @@ -349,7 +349,7 @@ Writable.prototype.uncork = function () { if (state.corked) { state.corked--; - if (!state.writing && !state.corked && !state.finished && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state); + if (!state.writing && !state.corked && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state); } }; @@ -591,7 +591,7 @@ Writable.prototype.end = function (chunk, encoding, cb) { } // ignore unnecessary end() calls. - if (!state.ending && !state.finished) endWritable(this, state, cb); + if (!state.ending) endWritable(this, state, cb); }; function needFinish(state) { @@ -652,11 +652,9 @@ function onCorkedFinish(corkReq, state, err) { cb(err); entry = entry.next; } - if (state.corkedRequestsFree) { - state.corkedRequestsFree.next = corkReq; - } else { - state.corkedRequestsFree = corkReq; - } + + // reuse the free corkReq. + state.corkedRequestsFree.next = corkReq; } Object.defineProperty(Writable.prototype, 'destroyed', { diff --git a/lib/internal/streams/BufferList.js b/lib/internal/streams/BufferList.js index aefc68bd90..5e080976c3 100644 --- a/lib/internal/streams/BufferList.js +++ b/lib/internal/streams/BufferList.js @@ -56,7 +56,6 @@ module.exports = function () { BufferList.prototype.concat = function concat(n) { if (this.length === 0) return Buffer.alloc(0); - if (this.length === 1) return this.head.data; var ret = Buffer.allocUnsafe(n >>> 0); var p = this.head; var i = 0; diff --git a/lib/internal/streams/destroy.js b/lib/internal/streams/destroy.js index 5a0a0d88ce..85a821407f 100644 --- a/lib/internal/streams/destroy.js +++ b/lib/internal/streams/destroy.js @@ -15,9 +15,15 @@ function destroy(err, cb) { if (readableDestroyed || writableDestroyed) { if (cb) { cb(err); - } else if (err && (!this._writableState || !this._writableState.errorEmitted)) { - pna.nextTick(emitErrorNT, this, err); + } else if (err) { + if (!this._writableState) { + pna.nextTick(emitErrorNT, this, err); + } else if (!this._writableState.errorEmitted) { + this._writableState.errorEmitted = true; + pna.nextTick(emitErrorNT, this, err); + } } + return this; } @@ -35,9 +41,11 @@ function destroy(err, cb) { this._destroy(err || null, function (err) { if (!cb && err) { - pna.nextTick(emitErrorNT, _this, err); - if (_this._writableState) { + if (!_this._writableState) { + pna.nextTick(emitErrorNT, _this, err); + } else if (!_this._writableState.errorEmitted) { _this._writableState.errorEmitted = true; + pna.nextTick(emitErrorNT, _this, err); } } else if (cb) { cb(err); @@ -59,6 +67,8 @@ function undestroy() { this._writableState.destroyed = false; this._writableState.ended = false; this._writableState.ending = false; + this._writableState.finalCalled = false; + this._writableState.prefinished = false; this._writableState.finished = false; this._writableState.errorEmitted = false; } diff --git a/package.json b/package.json index dbb1da6be5..514c178e92 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "readable-stream", - "version": "2.3.6", + "version": "2.3.8", "description": "Streams3, a user-land copy of the stream library from Node.js", "main": "readable.js", "dependencies": { diff --git a/test/common/README.md b/test/common/README.md index 67e4e4e48f..f9e5bdb156 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -26,6 +26,9 @@ This directory contains modules used to test the Node.js implementation. * [DNS module](#dns-module) * [Duplex pair helper](#duplex-pair-helper) * [Fixtures module](#fixtures-module) +* [HTTP2 module](#http2-module) +* [Internet module](#internet-module) +* [tmpdir module](#tmpdir-module) * [WPT module](#wpt-module) ## Benchmark Module @@ -249,6 +252,11 @@ Platform check for Windows. Platform check for Windows 32-bit on Windows 64-bit. +### isCPPSymbolsNotMapped +* [<Boolean>] + +Platform check for C++ symbols are mapped or not. + ### leakedGlobals() * return [<Array>] @@ -275,6 +283,17 @@ fail. If `fn` is not provided, an empty function will be used. +### mustCallAsync([fn][, exact]) +* `fn` [<Function>] +* `exact` [<Number>] default = 1 +* return [<Function>] + +The same as `mustCall()`, except that it is also checked that the Promise +returned by the function is fulfilled for each invocation of the function. + +The return value of the wrapped function is the return value of the original +function, if necessary wrapped as a promise. + ### mustCallAtLeast([fn][, minimum]) * `fn` [<Function>] default = () => {} * `minimum` [<Number>] default = 1 @@ -328,11 +347,6 @@ A port number for tests to use if one is needed. Logs '1..0 # Skipped: ' + `msg` -### refreshTmpDir() -* return [<String>] - -Deletes the testing 'tmp' directory and recreates it. - ### restoreStderr() Restore the original `process.stderr.write`. Used to restore `stderr` to its @@ -385,17 +399,12 @@ Platform normalizes the `pwd` command. Synchronous version of `spawnPwd`. -### tmpDir -* [<String>] - -The realpath of the 'tmp' directory. - ## Countdown Module The `Countdown` module provides a simple countdown mechanism for tests that require a particular action to be taken after a given number of completed tasks (for instance, shutting down an HTTP server after a specific number of -requests). +requests). The Countdown will fail the test if the remainder did not reach 0. ```js @@ -428,7 +437,26 @@ called before the callback is invoked. ## DNS Module -The `DNS` module provides a naïve DNS parser/serializer. +The `DNS` module provides utilities related to the `dns` built-in module. + +### errorLookupMock(code, syscall) + +* `code` [<String>] Defaults to `dns.mockedErrorCode`. +* `syscall` [<String>] Defaults to `dns.mockedSysCall`. +* return [<Function>] + + +A mock for the `lookup` option of `net.connect()` that would result in an error +with the `code` and the `syscall` specified. Returns a function that has the +same signature as `dns.lookup()`. + +### mockedErrorCode + +The default `code` of errors generated by `errorLookupMock`. + +### mockedSysCall + +The default `syscall` of errors generated by `errorLookupMock`. ### readDomainFromPacket(buffer, offset) @@ -508,6 +536,151 @@ Returns the result of Returns the result of `fs.readFileSync(path.join(fixtures.fixturesDir, 'keys', arg), 'enc')`. +## HTTP/2 Module + +The http2.js module provides a handful of utilities for creating mock HTTP/2 +frames for testing of HTTP/2 endpoints + + +```js +const http2 = require('../common/http2'); +``` + +### Class: Frame + +The `http2.Frame` is a base class that creates a `Buffer` containing a +serialized HTTP/2 frame header. + + +```js +// length is a 24-bit unsigned integer +// type is an 8-bit unsigned integer identifying the frame type +// flags is an 8-bit unsigned integer containing the flag bits +// id is the 32-bit stream identifier, if any. +const frame = new http2.Frame(length, type, flags, id); + +// Write the frame data to a socket +socket.write(frame.data); +``` + +The serialized `Buffer` may be retrieved using the `frame.data` property. + +### Class: DataFrame extends Frame + +The `http2.DataFrame` is a subclass of `http2.Frame` that serializes a `DATA` +frame. + + +```js +// id is the 32-bit stream identifier +// payload is a Buffer containing the DATA payload +// padlen is an 8-bit integer giving the number of padding bytes to include +// final is a boolean indicating whether the End-of-stream flag should be set, +// defaults to false. +const frame = new http2.DataFrame(id, payload, padlen, final); + +socket.write(frame.data); +``` + +### Class: HeadersFrame + +The `http2.HeadersFrame` is a subclass of `http2.Frame` that serializes a +`HEADERS` frame. + + +```js +// id is the 32-bit stream identifier +// payload is a Buffer containing the HEADERS payload (see either +// http2.kFakeRequestHeaders or http2.kFakeResponseHeaders). +// padlen is an 8-bit integer giving the number of padding bytes to include +// final is a boolean indicating whether the End-of-stream flag should be set, +// defaults to false. +const frame = new http2.HeadersFrame(id, payload, padlen, final); + +socket.write(frame.data); +``` + +### Class: SettingsFrame + +The `http2.SettingsFrame` is a subclass of `http2.Frame` that serializes an +empty `SETTINGS` frame. + + +```js +// ack is a boolean indicating whether or not to set the ACK flag. +const frame = new http2.SettingsFrame(ack); + +socket.write(frame.data); +``` + +### http2.kFakeRequestHeaders + +Set to a `Buffer` instance that contains a minimal set of serialized HTTP/2 +request headers to be used as the payload of a `http2.HeadersFrame`. + + +```js +const frame = new http2.HeadersFrame(1, http2.kFakeRequestHeaders, 0, true); + +socket.write(frame.data); +``` + +### http2.kFakeResponseHeaders + +Set to a `Buffer` instance that contains a minimal set of serialized HTTP/2 +response headers to be used as the payload a `http2.HeadersFrame`. + + +```js +const frame = new http2.HeadersFrame(1, http2.kFakeResponseHeaders, 0, true); + +socket.write(frame.data); +``` + +### http2.kClientMagic + +Set to a `Buffer` containing the preamble bytes an HTTP/2 client must send +upon initial establishment of a connection. + + +```js +socket.write(http2.kClientMagic); +``` + +## Internet Module + +The `common/internet` module provides utilities for working with +internet-related tests. + +### internet.addresses + +* [<Object>] + * `INET_HOST` [<String>] A generic host that has registered common + DNS records, supports both IPv4 and IPv6, and provides basic HTTP/HTTPS + services + * `INET4_HOST` [<String>] A host that provides IPv4 services + * `INET6_HOST` [<String>] A host that provides IPv6 services + * `INET4_IP` [<String>] An accessible IPv4 IP, defaults to the + Google Public DNS IPv4 address + * `INET6_IP` [<String>] An accessible IPv6 IP, defaults to the + Google Public DNS IPv6 address + * `INVALID_HOST` [<String>] An invalid host that cannot be resolved + * `MX_HOST` [<String>] A host with MX records registered + * `SRV_HOST` [<String>] A host with SRV records registered + * `PTR_HOST` [<String>] A host with PTR records registered + * `NAPTR_HOST` [<String>] A host with NAPTR records registered + * `SOA_HOST` [<String>] A host with SOA records registered + * `CNAME_HOST` [<String>] A host with CNAME records registered + * `NS_HOST` [<String>] A host with NS records registered + * `TXT_HOST` [<String>] A host with TXT records registered + * `DNS4_SERVER` [<String>] An accessible IPv4 DNS server + * `DNS6_SERVER` [<String>] An accessible IPv6 DNS server + +A set of addresses for internet-related tests. All properties are configurable +via `NODE_TEST_*` environment variables. For example, to configure +`internet.addresses.INET_HOST`, set the environment +variable `NODE_TEST_INET_HOST` to a specified host. + ## WPT Module The wpt.js module is a port of parts of @@ -517,6 +690,7 @@ Node.js implementation with tests from [W3C Web Platform Tests](https://github.com/w3c/web-platform-tests). + [<Array>]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array [<ArrayBufferView[]>]: https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView [<Boolean>]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type diff --git a/test/common/countdown.js b/test/common/countdown.js index 411fb69d2e..7590becdcf 100644 --- a/test/common/countdown.js +++ b/test/common/countdown.js @@ -34,6 +34,7 @@ var objectKeys = objectKeys || function (obj) { var assert = require('assert'); var kLimit = Symbol('limit'); var kCallback = Symbol('callback'); +var common = require('./'); var Countdown = function () { function Countdown(limit, cb) { @@ -42,7 +43,7 @@ var Countdown = function () { assert.strictEqual(typeof limit, 'number'); assert.strictEqual(typeof cb, 'function'); this[kLimit] = limit; - this[kCallback] = cb; + this[kCallback] = common.mustCall(cb); } Countdown.prototype.dec = function dec() { diff --git a/test/common/dns.js b/test/common/dns.js index 388c8df83d..aea1e6a947 100644 --- a/test/common/dns.js +++ b/test/common/dns.js @@ -29,8 +29,6 @@ var objectKeys = objectKeys || function (obj) { }; /**/ -// Naïve DNS parser/serializer. - var assert = require('assert'); var os = require('os'); @@ -50,6 +48,8 @@ var classes = { IN: 1 }; +// Naïve DNS parser/serializer. + function readDomainFromPacket(buffer, offset) { assert.ok(offset < buffer.length); var length = buffer[offset]; @@ -409,7 +409,32 @@ function writeDNSPacket(parsed) { })); } -module.exports = { types: types, classes: classes, writeDNSPacket: writeDNSPacket, parseDNSPacket: parseDNSPacket }; +var mockedErrorCode = 'ENOTFOUND'; +var mockedSysCall = 'getaddrinfo'; + +function errorLookupMock() { + var code = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : mockedErrorCode; + var syscall = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : mockedSysCall; + + return function lookupWithError(host, dnsopts, cb) { + var err = new Error(syscall + ' ' + code + ' ' + host); + err.code = code; + err.errno = code; + err.syscall = syscall; + err.hostname = host; + cb(err); + }; +} + +module.exports = { + types: types, + classes: classes, + writeDNSPacket: writeDNSPacket, + parseDNSPacket: parseDNSPacket, + errorLookupMock: errorLookupMock, + mockedErrorCode: mockedErrorCode, + mockedSysCall: mockedSysCall +}; function forEach(xs, f) { for (var i = 0, l = xs.length; i < l; i++) { diff --git a/test/common/http2.js b/test/common/http2.js new file mode 100644 index 0000000000..dba0bd8f9b --- /dev/null +++ b/test/common/http2.js @@ -0,0 +1,237 @@ +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +/**/ +require('babel-polyfill'); +var util = require('util'); +for (var i in util) { + exports[i] = util[i]; +} /**/ /**/ +if (!global.setImmediate) { + global.setImmediate = function setImmediate(fn) { + return setTimeout(fn.bind.apply(fn, arguments), 4); + }; +} +if (!global.clearImmediate) { + global.clearImmediate = function clearImmediate(i) { + return clearTimeout(i); + }; +} +/**/ +/* eslint-disable required-modules */ +'use strict'; + +/**/ +var objectKeys = objectKeys || function (obj) { + var keys = []; + for (var key in obj) { + keys.push(key); + }return keys; +}; +/**/ + +// An HTTP/2 testing tool used to create mock frames for direct testing +// of HTTP/2 endpoints. + +var kFrameData = Symbol('frame-data'); +var FLAG_EOS = 0x1; +var FLAG_ACK = 0x1; +var FLAG_EOH = 0x4; +var FLAG_PADDED = 0x8; +var PADDING = Buffer.alloc(255); + +var kClientMagic = Buffer.from('505249202a20485454502f322' + 'e300d0a0d0a534d0d0a0d0a', 'hex'); + +var kFakeRequestHeaders = Buffer.from('828684410f7777772e65' + '78616d706c652e636f6d', 'hex'); + +var kFakeResponseHeaders = Buffer.from('4803333032580770726976617465611d' + '4d6f6e2c203231204f63742032303133' + '2032303a31333a323120474d546e1768' + '747470733a2f2f7777772e6578616d70' + '6c652e636f6d', 'hex'); + +function isUint32(val) { + return val >>> 0 === val; +} + +function isUint24(val) { + return val >>> 0 === val && val <= 0xFFFFFF; +} + +function isUint8(val) { + return val >>> 0 === val && val <= 0xFF; +} + +function write32BE(array, pos, val) { + if (!isUint32(val)) throw new RangeError('val is not a 32-bit number'); + array[pos++] = val >> 24 & 0xff; + array[pos++] = val >> 16 & 0xff; + array[pos++] = val >> 8 & 0xff; + array[pos++] = val & 0xff; +} + +function write24BE(array, pos, val) { + if (!isUint24(val)) throw new RangeError('val is not a 24-bit number'); + array[pos++] = val >> 16 & 0xff; + array[pos++] = val >> 8 & 0xff; + array[pos++] = val & 0xff; +} + +function write8(array, pos, val) { + if (!isUint8(val)) throw new RangeError('val is not an 8-bit number'); + array[pos] = val; +} + +var Frame = function () { + function Frame(length, type, flags, id) { + _classCallCheck(this, Frame); + + this[kFrameData] = Buffer.alloc(9); + write24BE(this[kFrameData], 0, length); + write8(this[kFrameData], 3, type); + write8(this[kFrameData], 4, flags); + write32BE(this[kFrameData], 5, id); + } + + _createClass(Frame, [{ + key: 'data', + get: function () { + return this[kFrameData]; + } + }]); + + return Frame; +}(); + +var SettingsFrame = function (_Frame) { + _inherits(SettingsFrame, _Frame); + + function SettingsFrame() { + var ack = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; + + _classCallCheck(this, SettingsFrame); + + var flags = 0; + if (ack) flags |= FLAG_ACK; + return _possibleConstructorReturn(this, _Frame.call(this, 0, 4, flags, 0)); + } + + return SettingsFrame; +}(Frame); + +var DataFrame = function (_Frame2) { + _inherits(DataFrame, _Frame2); + + function DataFrame(id, payload) { + var padlen = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; + var final = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; + + _classCallCheck(this, DataFrame); + + var len = payload.length; + var flags = 0; + if (final) flags |= FLAG_EOS; + var buffers = [payload]; + if (padlen > 0) { + buffers.unshift(Buffer.from([padlen])); + buffers.push(PADDING.slice(0, padlen)); + len += padlen + 1; + flags |= FLAG_PADDED; + } + + var _this2 = _possibleConstructorReturn(this, _Frame2.call(this, len, 0, flags, id)); + + buffers.unshift(_this2[kFrameData]); + _this2[kFrameData] = Buffer.concat(buffers); + return _this2; + } + + return DataFrame; +}(Frame); + +var HeadersFrame = function (_Frame3) { + _inherits(HeadersFrame, _Frame3); + + function HeadersFrame(id, payload) { + var padlen = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; + var final = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; + + _classCallCheck(this, HeadersFrame); + + var len = payload.length; + var flags = FLAG_EOH; + if (final) flags |= FLAG_EOS; + var buffers = [payload]; + if (padlen > 0) { + buffers.unshift(Buffer.from([padlen])); + buffers.push(PADDING.slice(0, padlen)); + len += padlen + 1; + flags |= FLAG_PADDED; + } + + var _this3 = _possibleConstructorReturn(this, _Frame3.call(this, len, 1, flags, id)); + + buffers.unshift(_this3[kFrameData]); + _this3[kFrameData] = Buffer.concat(buffers); + return _this3; + } + + return HeadersFrame; +}(Frame); + +var PingFrame = function (_Frame4) { + _inherits(PingFrame, _Frame4); + + function PingFrame() { + var ack = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; + + _classCallCheck(this, PingFrame); + + var buffers = [Buffer.alloc(8)]; + + var _this4 = _possibleConstructorReturn(this, _Frame4.call(this, 8, 6, ack ? 1 : 0, 0)); + + buffers.unshift(_this4[kFrameData]); + _this4[kFrameData] = Buffer.concat(buffers); + return _this4; + } + + return PingFrame; +}(Frame); + +var AltSvcFrame = function (_Frame5) { + _inherits(AltSvcFrame, _Frame5); + + function AltSvcFrame(size) { + _classCallCheck(this, AltSvcFrame); + + var buffers = [Buffer.alloc(size)]; + + var _this5 = _possibleConstructorReturn(this, _Frame5.call(this, size, 10, 0, 0)); + + buffers.unshift(_this5[kFrameData]); + _this5[kFrameData] = Buffer.concat(buffers); + return _this5; + } + + return AltSvcFrame; +}(Frame); + +module.exports = { + Frame: Frame, + AltSvcFrame: AltSvcFrame, + DataFrame: DataFrame, + HeadersFrame: HeadersFrame, + SettingsFrame: SettingsFrame, + PingFrame: PingFrame, + kFakeRequestHeaders: kFakeRequestHeaders, + kFakeResponseHeaders: kFakeResponseHeaders, + kClientMagic: kClientMagic +}; + +function forEach(xs, f) { + for (var i = 0, l = xs.length; i < l; i++) { + f(xs[i], i); + } +} \ No newline at end of file diff --git a/test/common/index.js b/test/common/index.js index 33fa11e8d6..15cd964f84 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -75,13 +75,10 @@ var Timer = { now: function () {} }; var _require2 = require('./fixtures'), fixturesDir = _require2.fixturesDir; -var testRoot = process.env.NODE_TEST_DIR ? fs.realpathSync(process.env.NODE_TEST_DIR) : path.resolve(__dirname, '..'); +var tmpdir = require('./tmpdir'); var noop = function () {}; -// Using a `.` prefixed name, which is the convention for "hidden" on POSIX, -// gets tools to ignore it by default or by simple rules, especially eslint. -var tmpDirName = '.tmp'; // PORT should match the definition in test/testpy/__init__.py. exports.PORT = +process.env.NODE_COMMON_PORT || 12346; exports.isWindows = process.platform === 'win32'; @@ -90,8 +87,10 @@ exports.isAIX = process.platform === 'aix'; exports.isLinuxPPCBE = process.platform === 'linux' && process.arch === 'ppc64' && os.endianness() === 'BE'; exports.isSunOS = process.platform === 'sunos'; exports.isFreeBSD = process.platform === 'freebsd'; +exports.isOpenBSD = process.platform === 'openbsd'; exports.isLinux = process.platform === 'linux'; -exports.isOSX = process.platform === 'darwin'; +var isOSX = exports.isOSX = process.platform === 'darwin'; +exports.isOSXMojave = isOSX && os.release().startsWith('18'); exports.enoughTestMem = os.totalmem() > 0x70000000; /* 1.75 Gb */ var cpus = os.cpus(); @@ -122,7 +121,7 @@ if (process.env.NODE_TEST_WITH_ASYNC_HOOKS) { var _async_wrap = process.binding('async_wrap'); process.on('exit', function () { - // itterate through handles to make sure nothing crashes + // iterate through handles to make sure nothing crashes for (var k in initHandles) { util.inspect(initHandles[k]); } @@ -162,55 +161,6 @@ if (process.env.NODE_TEST_WITH_ASYNC_HOOKS) { }).enable();*/ } -function rimrafSync(p) { - var st = void 0; - try { - st = fs.lstatSync(p); - } catch (e) { - if (e.code === 'ENOENT') return; - } - - try { - if (st && st.isDirectory()) rmdirSync(p, null);else fs.unlinkSync(p); - } catch (e) { - if (e.code === 'ENOENT') return; - if (e.code === 'EPERM') return rmdirSync(p, e); - if (e.code !== 'EISDIR') throw e; - rmdirSync(p, e); - } -} - -function rmdirSync(p, originalEr) { - try { - fs.rmdirSync(p); - } catch (e) { - if (e.code === 'ENOTDIR') throw originalEr; - if (e.code === 'ENOTEMPTY' || e.code === 'EEXIST' || e.code === 'EPERM') { - var enc = exports.isLinux ? 'buffer' : 'utf8'; - forEach(fs.readdirSync(p, enc), function (f) { - if (f instanceof Buffer) { - var buf = Buffer.concat([Buffer.from(p), Buffer.from(path.sep), f]); - rimrafSync(buf); - } else { - rimrafSync(path.join(p, f)); - } - }); - fs.rmdirSync(p); - } - } -} - -exports.refreshTmpDir = function () { - rimrafSync(exports.tmpDir); - fs.mkdirSync(exports.tmpDir); -}; - -if (process.env.TEST_THREAD_ID) { - exports.PORT += process.env.TEST_THREAD_ID * 100; - tmpDirName += '.' + process.env.TEST_THREAD_ID; -} -exports.tmpDir = path.join(testRoot, tmpDirName); - var opensslCli = null; var inFreeBSDJail = null; var localhostIPv4 = null; @@ -307,7 +257,7 @@ if (exports.isLinux) { } /**/ { - var localRelative = path.relative(process.cwd(), exports.tmpDir + '/'); + var localRelative = path.relative(process.cwd(), tmpdir.path + '/'); var pipePrefix = exports.isWindows ? '\\\\.\\pipe\\' : localRelative; var pipeName = 'node-test.' + process.pid + '.sock'; exports.PIPE = path.join(pipePrefix, pipeName); @@ -532,6 +482,14 @@ exports.mustCallAtLeast = function (fn, minimum) { return _mustCallInner(fn, minimum, 'minimum'); }; +exports.mustCallAsync = function (fn, exact) { + return exports.mustCall(function () { + return Promise.resolve(fn.apply(undefined, arguments)).then(exports.mustCall(function (val) { + return val; + })); + }, exact); +}; + function _mustCallInner(fn) { var _context; @@ -595,7 +553,7 @@ exports.canCreateSymLink = function () { // whoami.exe needs to be the one from System32 // If unix tools are in the path, they can shadow the one we want, // so use the full path while executing whoami - var whoamiPath = path.join(process.env['SystemRoot'], 'System32', 'whoami.exe'); + var whoamiPath = path.join(process.env.SystemRoot, 'System32', 'whoami.exe'); var err = false; var output = ''; @@ -779,7 +737,14 @@ exports.expectsError = function expectsError(fn, settings, exact) { fn = undefined; } function innerFn(error) { + if (arguments.length !== 1) { + // Do not use `assert.strictEqual()` to prevent `util.inspect` from + // always being called. + assert.fail('Expected one argument, got ' + util.inspect(arguments)); + } assert.strictEqual(error.code, settings.code); + var descriptor = Object.getOwnPropertyDescriptor(error, 'message'); + assert.strictEqual(descriptor.enumerable, false, 'The error message should be non-enumerable'); if ('type' in settings) { var type = settings.type; if (type !== Error && !Error.isPrototypeOf(type)) { @@ -933,6 +898,8 @@ exports.firstInvalidFD = function firstInvalidFD() { return fd; }; +exports.isCPPSymbolsNotMapped = exports.isWindows || exports.isSunOS || exports.isAIX || exports.isLinuxPPCBE || exports.isFreeBSD; + function forEach(xs, f) { for (var i = 0, l = xs.length; i < l; i++) { f(xs[i], i); diff --git a/test/common/inspector-helper.js b/test/common/inspector-helper.js index 3e4f16573a..8937c95aa6 100644 --- a/test/common/inspector-helper.js +++ b/test/common/inspector-helper.js @@ -218,9 +218,10 @@ var InspectorSession = function () { if (message.result) resolve(message.result);else reject(message.error); } else { if (message.method === 'Debugger.scriptParsed') { - var script = message['params']; - var scriptId = script['scriptId']; - var _url = script['url']; + var _message$params = message.params, + scriptId = _message$params.scriptId, + _url = _message$params.url; + this._scriptsIdsByUrl.set(scriptId, _url); if (_url === _MAINSCRIPT) this.mainScriptId = scriptId; } @@ -240,11 +241,11 @@ var InspectorSession = function () { var _this2 = this; var msg = JSON.parse(JSON.stringify(message)); // Clone! - msg['id'] = this._nextId++; + msg.id = this._nextId++; if (DEBUG) console.log('[sent]', JSON.stringify(msg)); var responsePromise = new Promise(function (resolve, reject) { - _this2._commandResponsePromises.set(msg['id'], { resolve: resolve, reject: reject }); + _this2._commandResponsePromises.set(msg.id, { resolve: resolve, reject: reject }); }); return new Promise(function (resolve) { @@ -294,12 +295,13 @@ var InspectorSession = function () { return notification; }; - InspectorSession.prototype._isBreakOnLineNotification = function _isBreakOnLineNotification(message, line, url) { - if ('Debugger.paused' === message['method']) { - var callFrame = message['params']['callFrames'][0]; - var location = callFrame['location']; - assert.strictEqual(url, this._scriptsIdsByUrl.get(location['scriptId'])); - assert.strictEqual(line, location['lineNumber']); + InspectorSession.prototype._isBreakOnLineNotification = function _isBreakOnLineNotification(message, line, expectedScriptPath) { + if ('Debugger.paused' === message.method) { + var callFrame = message.params.callFrames[0]; + var location = callFrame.location; + var scriptPath = this._scriptsIdsByUrl.get(location.scriptId); + assert.strictEqual(scriptPath.toString(), expectedScriptPath.toString(), scriptPath + ' !== ' + expectedScriptPath); + assert.strictEqual(line, location.lineNumber); return true; } }; @@ -314,19 +316,19 @@ var InspectorSession = function () { InspectorSession.prototype._matchesConsoleOutputNotification = function _matchesConsoleOutputNotification(notification, type, values) { if (!Array.isArray(values)) values = [values]; - if ('Runtime.consoleAPICalled' === notification['method']) { - var params = notification['params']; - if (params['type'] === type) { + if ('Runtime.consoleAPICalled' === notification.method) { + var params = notification.params; + if (params.type === type) { var _i2 = 0; var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { - for (var _iterator2 = params['args'][Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { + for (var _iterator2 = params.args[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var value = _step2.value; - if (value['value'] !== values[_i2++]) return false; + if (value.value !== values[_i2++]) return false; } } catch (err) { _didIteratorError2 = true; @@ -480,7 +482,7 @@ var NodeInstance = function () { NodeInstance.prototype.connectInspectorSession = async function connectInspectorSession() { console.log('[test]', 'Connecting to a child Node process'); var response = await this.httpGet(null, '/json/list'); - var url = response[0]['webSocketDebuggerUrl']; + var url = response[0].webSocketDebuggerUrl; return this.wsHandshake(url); }; diff --git a/test/common/internet.js b/test/common/internet.js new file mode 100644 index 0000000000..ec9bcfbcdc --- /dev/null +++ b/test/common/internet.js @@ -0,0 +1,107 @@ +/**/ +require('babel-polyfill'); +var util = require('util'); +for (var i in util) { + exports[i] = util[i]; +} /**/ /**/ +if (!global.setImmediate) { + global.setImmediate = function setImmediate(fn) { + return setTimeout(fn.bind.apply(fn, arguments), 4); + }; +} +if (!global.clearImmediate) { + global.clearImmediate = function clearImmediate(i) { + return clearTimeout(i); + }; +} +/**/ +/* eslint-disable required-modules */ +'use strict'; + +/**/ +var objectKeys = objectKeys || function (obj) { + var keys = []; + for (var key in obj) { + keys.push(key); + }return keys; +}; +/**/ + +// Utilities for internet-related tests + +var addresses = { + // A generic host that has registered common DNS records, + // supports both IPv4 and IPv6, and provides basic HTTP/HTTPS services + INET_HOST: 'nodejs.org', + // A host that provides IPv4 services + INET4_HOST: 'nodejs.org', + // A host that provides IPv6 services + INET6_HOST: 'nodejs.org', + // An accessible IPv4 IP, + // defaults to the Google Public DNS IPv4 address + INET4_IP: '8.8.8.8', + // An accessible IPv6 IP, + // defaults to the Google Public DNS IPv6 address + INET6_IP: '2001:4860:4860::8888', + // An invalid host that cannot be resolved + // See https://tools.ietf.org/html/rfc2606#section-2 + INVALID_HOST: 'something.invalid', + // A host with MX records registered + MX_HOST: 'nodejs.org', + // A host with SRV records registered + SRV_HOST: '_jabber._tcp.google.com', + // A host with PTR records registered + PTR_HOST: '8.8.8.8.in-addr.arpa', + // A host with NAPTR records registered + NAPTR_HOST: 'sip2sip.info', + // A host with SOA records registered + SOA_HOST: 'nodejs.org', + // A host with CNAME records registered + CNAME_HOST: 'blog.nodejs.org', + // A host with NS records registered + NS_HOST: 'nodejs.org', + // A host with TXT records registered + TXT_HOST: 'nodejs.org', + // An accessible IPv4 DNS server + DNS4_SERVER: '8.8.8.8', + // An accessible IPv4 DNS server + DNS6_SERVER: '2001:4860:4860::8888' +}; + +var _iteratorNormalCompletion = true; +var _didIteratorError = false; +var _iteratorError = undefined; + +try { + for (var _iterator = objectKeys(addresses)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var key = _step.value; + + var envName = 'NODE_TEST_' + key; + if (process.env[envName]) { + addresses[key] = process.env[envName]; + } + } +} catch (err) { + _didIteratorError = true; + _iteratorError = err; +} finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } +} + +module.exports = { + addresses: addresses +}; + +function forEach(xs, f) { + for (var i = 0, l = xs.length; i < l; i++) { + f(xs[i], i); + } +} \ No newline at end of file diff --git a/test/common/shared-lib-util.js b/test/common/shared-lib-util.js new file mode 100644 index 0000000000..72c0cc719d --- /dev/null +++ b/test/common/shared-lib-util.js @@ -0,0 +1,70 @@ +/**/ +require('babel-polyfill'); +var util = require('util'); +for (var i in util) { + exports[i] = util[i]; +} /**/ /**/ +if (!global.setImmediate) { + global.setImmediate = function setImmediate(fn) { + return setTimeout(fn.bind.apply(fn, arguments), 4); + }; +} +if (!global.clearImmediate) { + global.clearImmediate = function clearImmediate(i) { + return clearTimeout(i); + }; +} +/**/ +'use strict'; + +/**/ +var objectKeys = objectKeys || function (obj) { + var keys = []; + for (var key in obj) { + keys.push(key); + }return keys; +}; +/**/ + +var common = require('../common'); +var path = require('path'); + +// If node executable is linked to shared lib, need to take care about the +// shared lib path. +exports.addLibraryPath = function (env) { + if (!process.config.variables.node_shared) { + return; + } + + env = env || process.env; + + env.LD_LIBRARY_PATH = (env.LD_LIBRARY_PATH ? env.LD_LIBRARY_PATH + path.delimiter : '') + path.join(path.dirname(process.execPath), 'lib.target'); + // For AIX. + env.LIBPATH = (env.LIBPATH ? env.LIBPATH + path.delimiter : '') + path.join(path.dirname(process.execPath), 'lib.target'); + // For Mac OSX. + env.DYLD_LIBRARY_PATH = (env.DYLD_LIBRARY_PATH ? env.DYLD_LIBRARY_PATH + path.delimiter : '') + path.dirname(process.execPath); + // For Windows. + env.PATH = (env.PATH ? env.PATH + path.delimiter : '') + path.dirname(process.execPath); +}; + +// Get the full path of shared lib. +exports.getSharedLibPath = function () { + if (common.isWindows) { + return path.join(path.dirname(process.execPath), 'node.dll'); + } else if (common.isOSX) { + return path.join(path.dirname(process.execPath), 'libnode.' + process.config.variables.shlib_suffix); + } else { + return path.join(path.dirname(process.execPath), 'lib.target', 'libnode.' + process.config.variables.shlib_suffix); + } +}; + +// Get the binary path of stack frames. +exports.getBinaryPath = function () { + return process.config.variables.node_shared ? exports.getSharedLibPath() : process.execPath; +}; + +function forEach(xs, f) { + for (var i = 0, l = xs.length; i < l; i++) { + f(xs[i], i); + } +} \ No newline at end of file diff --git a/test/common/tls.js b/test/common/tls.js new file mode 100644 index 0000000000..7f884f1470 --- /dev/null +++ b/test/common/tls.js @@ -0,0 +1,210 @@ +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +/**/ +require('babel-polyfill'); +var util = require('util'); +for (var i in util) { + exports[i] = util[i]; +} /**/ /**/ +if (!global.setImmediate) { + global.setImmediate = function setImmediate(fn) { + return setTimeout(fn.bind.apply(fn, arguments), 4); + }; +} +if (!global.clearImmediate) { + global.clearImmediate = function clearImmediate(i) { + return clearTimeout(i); + }; +} +/**/ +/* eslint-disable required-modules, crypto-check */ + +'use strict'; + +/**/ +var objectKeys = objectKeys || function (obj) { + var keys = []; + for (var key in obj) { + keys.push(key); + }return keys; +}; +/**/ + +var crypto = require('crypto'); +var net = require('net'); + +exports.ccs = Buffer.from('140303000101', 'hex'); + +var TestTLSSocket = function (_net$Socket) { + _inherits(TestTLSSocket, _net$Socket); + + function TestTLSSocket(server_cert) { + _classCallCheck(this, TestTLSSocket); + + var _this = _possibleConstructorReturn(this, _net$Socket.call(this)); + + _this.server_cert = server_cert; + _this.version = Buffer.from('0303', 'hex'); + _this.handshake_list = []; + // AES128-GCM-SHA256 + _this.ciphers = Buffer.from('000002009c0', 'hex'); + _this.pre_master_secret = Buffer.concat([_this.version, crypto.randomBytes(46)]); + _this.master_secret = null; + _this.write_seq = 0; + _this.client_random = crypto.randomBytes(32); + + _this.on('handshake', function (msg) { + _this.handshake_list.push(msg); + }); + + _this.on('server_random', function (server_random) { + _this.master_secret = PRF12('sha256', _this.pre_master_secret, 'master secret', Buffer.concat([_this.client_random, server_random]), 48); + var key_block = PRF12('sha256', _this.master_secret, 'key expansion', Buffer.concat([server_random, _this.client_random]), 40); + _this.client_writeKey = key_block.slice(0, 16); + _this.client_writeIV = key_block.slice(32, 36); + }); + return _this; + } + + TestTLSSocket.prototype.createClientHello = function createClientHello() { + var compressions = Buffer.from('0100', 'hex'); // null + var msg = addHandshakeHeader(0x01, Buffer.concat([this.version, this.client_random, this.ciphers, compressions])); + this.emit('handshake', msg); + return addRecordHeader(0x16, msg); + }; + + TestTLSSocket.prototype.createClientKeyExchange = function createClientKeyExchange() { + var encrypted_pre_master_secret = crypto.publicEncrypt({ + key: this.server_cert, + padding: crypto.constants.RSA_PKCS1_PADDING + }, this.pre_master_secret); + var length = Buffer.alloc(2); + length.writeUIntBE(encrypted_pre_master_secret.length, 0, 2); + var msg = addHandshakeHeader(0x10, Buffer.concat([length, encrypted_pre_master_secret])); + this.emit('handshake', msg); + return addRecordHeader(0x16, msg); + }; + + TestTLSSocket.prototype.createFinished = function createFinished() { + var shasum = crypto.createHash('sha256'); + shasum.update(Buffer.concat(this.handshake_list)); + var message_hash = shasum.digest(); + var r = PRF12('sha256', this.master_secret, 'client finished', message_hash, 12); + var msg = addHandshakeHeader(0x14, r); + this.emit('handshake', msg); + return addRecordHeader(0x16, msg); + }; + + TestTLSSocket.prototype.createIllegalHandshake = function createIllegalHandshake() { + var illegal_handshake = Buffer.alloc(5); + return addRecordHeader(0x16, illegal_handshake); + }; + + TestTLSSocket.prototype.parseTLSFrame = function parseTLSFrame(buf) { + var offset = 0; + var record = buf.slice(offset, 5); + var type = record[0]; + var length = record.slice(3, 5).readUInt16BE(0); + offset += 5; + var remaining = buf.slice(offset, offset + length); + if (type === 0x16) { + do { + remaining = this.parseTLSHandshake(remaining); + } while (remaining.length > 0); + } + offset += length; + return buf.slice(offset); + }; + + TestTLSSocket.prototype.parseTLSHandshake = function parseTLSHandshake(buf) { + var offset = 0; + var handshake_type = buf[offset]; + if (handshake_type === 0x02) { + var server_random = buf.slice(6, 6 + 32); + this.emit('server_random', server_random); + } + offset += 1; + var length = buf.readUIntBE(offset, 3); + offset += 3; + var handshake = buf.slice(0, offset + length); + this.emit('handshake', handshake); + offset += length; + var remaining = buf.slice(offset); + return remaining; + }; + + TestTLSSocket.prototype.encrypt = function encrypt(plain) { + var type = plain.slice(0, 1); + var version = plain.slice(1, 3); + var nonce = crypto.randomBytes(8); + var iv = Buffer.concat([this.client_writeIV.slice(0, 4), nonce]); + var bob = crypto.createCipheriv('aes-128-gcm', this.client_writeKey, iv); + var write_seq = Buffer.alloc(8); + write_seq.writeUInt32BE(this.write_seq++, 4); + var aad = Buffer.concat([write_seq, plain.slice(0, 5)]); + bob.setAAD(aad); + var encrypted1 = bob.update(plain.slice(5)); + var encrypted = Buffer.concat([encrypted1, bob.final()]); + var tag = bob.getAuthTag(); + var length = Buffer.alloc(2); + length.writeUInt16BE(nonce.length + encrypted.length + tag.length, 0); + return Buffer.concat([type, version, length, nonce, encrypted, tag]); + }; + + return TestTLSSocket; +}(net.Socket); + +function addRecordHeader(type, frame) { + var record_layer = Buffer.from('0003030000', 'hex'); + record_layer[0] = type; + record_layer.writeUInt16BE(frame.length, 3); + return Buffer.concat([record_layer, frame]); +} + +function addHandshakeHeader(type, msg) { + var handshake_header = Buffer.alloc(4); + handshake_header[0] = type; + handshake_header.writeUIntBE(msg.length, 1, 3); + return Buffer.concat([handshake_header, msg]); +} + +function PRF12(algo, secret, label, seed, size) { + var newSeed = Buffer.concat([Buffer.from(label, 'utf8'), seed]); + return P_hash(algo, secret, newSeed, size); +} + +function P_hash(algo, secret, seed, size) { + var result = Buffer.alloc(size); + var hmac = crypto.createHmac(algo, secret); + hmac.update(seed); + var a = hmac.digest(); + var j = 0; + while (j < size) { + hmac = crypto.createHmac(algo, secret); + hmac.update(a); + hmac.update(seed); + var b = hmac.digest(); + var todo = b.length; + if (j + todo > size) { + todo = size - j; + } + b.copy(result, j, 0, todo); + j += todo; + hmac = crypto.createHmac(algo, secret); + hmac.update(a); + a = hmac.digest(); + } + return result; +} + +exports.TestTLSSocket = TestTLSSocket; + +function forEach(xs, f) { + for (var i = 0, l = xs.length; i < l; i++) { + f(xs[i], i); + } +} \ No newline at end of file diff --git a/test/common/tmpdir.js b/test/common/tmpdir.js new file mode 100644 index 0000000000..4753cbe7e5 --- /dev/null +++ b/test/common/tmpdir.js @@ -0,0 +1,90 @@ +/**/ +require('babel-polyfill'); +var util = require('util'); +for (var i in util) { + exports[i] = util[i]; +} /**/ /**/ +if (!global.setImmediate) { + global.setImmediate = function setImmediate(fn) { + return setTimeout(fn.bind.apply(fn, arguments), 4); + }; +} +if (!global.clearImmediate) { + global.clearImmediate = function clearImmediate(i) { + return clearTimeout(i); + }; +} +/**/ +/* eslint-disable required-modules */ +'use strict'; + +/**/ +var objectKeys = objectKeys || function (obj) { + var keys = []; + for (var key in obj) { + keys.push(key); + }return keys; +}; +/**/ + +var fs = require('fs'); +var path = require('path'); + +function rimrafSync(p) { + var st = void 0; + try { + st = fs.lstatSync(p); + } catch (e) { + if (e.code === 'ENOENT') return; + } + + try { + if (st && st.isDirectory()) rmdirSync(p, null);else fs.unlinkSync(p); + } catch (e) { + if (e.code === 'ENOENT') return; + if (e.code === 'EPERM') return rmdirSync(p, e); + if (e.code !== 'EISDIR') throw e; + rmdirSync(p, e); + } +} + +function rmdirSync(p, originalEr) { + try { + fs.rmdirSync(p); + } catch (e) { + if (e.code === 'ENOTDIR') throw originalEr; + if (e.code === 'ENOTEMPTY' || e.code === 'EEXIST' || e.code === 'EPERM') { + var enc = process.platform === 'linux' ? 'buffer' : 'utf8'; + forEach(fs.readdirSync(p, enc), function (f) { + if (f instanceof Buffer) { + var buf = Buffer.concat([Buffer.from(p), Buffer.from(path.sep), f]); + rimrafSync(buf); + } else { + rimrafSync(path.join(p, f)); + } + }); + fs.rmdirSync(p); + } + } +} + +var testRoot = process.env.NODE_TEST_DIR ? fs.realpathSync(process.env.NODE_TEST_DIR) : path.resolve(__dirname, '..'); + +// Using a `.` prefixed name, which is the convention for "hidden" on POSIX, +// gets tools to ignore it by default or by simple rules, especially eslint. +var tmpdirName = '.tmp'; +if (process.env.TEST_THREAD_ID) { + tmpdirName += '.' + process.env.TEST_THREAD_ID; +} +exports.path = path.join(testRoot, tmpdirName); + +exports.refresh = function () { + rimrafSync(exports.path); + fs.mkdirSync(exports.path); +}; + +function forEach(xs, f) { + for (var i = 0, l = xs.length; i < l; i++) { + f(xs[i], i); + } +} \ No newline at end of file diff --git a/test/parallel/test-stream-buffer-list.js b/test/parallel/test-stream-buffer-list.js index 531658e81e..54a4c7cc71 100644 --- a/test/parallel/test-stream-buffer-list.js +++ b/test/parallel/test-stream-buffer-list.js @@ -16,14 +16,19 @@ assert.strictEqual(emptyList.join(','), ''); assert.deepStrictEqual(emptyList.concat(0), bufferShim.alloc(0)); +var buf = bufferShim.from('foo'); + // Test buffer list with one element. var list = new BufferList(); -list.push('foo'); +list.push(buf); + +var copy = list.concat(3); -assert.strictEqual(list.concat(1), 'foo'); +assert.notStrictEqual(copy, buf); +assert.deepStrictEqual(copy, buf); assert.strictEqual(list.join(','), 'foo'); var shifted = list.shift(); -assert.strictEqual(shifted, 'foo'); +assert.strictEqual(shifted, buf); assert.deepStrictEqual(list, new BufferList()); \ No newline at end of file diff --git a/test/parallel/test-stream-pipe-unpipe-streams.js b/test/parallel/test-stream-pipe-unpipe-streams.js index 6cc3a10491..79f81f3d7d 100644 --- a/test/parallel/test-stream-pipe-unpipe-streams.js +++ b/test/parallel/test-stream-pipe-unpipe-streams.js @@ -34,4 +34,46 @@ source.unpipe(dest2); source.unpipe(dest1); -assert.strictEqual(source._readableState.pipes, null); \ No newline at end of file +assert.strictEqual(source._readableState.pipes, null); + +{ + // test `cleanup()` if we unpipe all streams. + var _source = Readable({ read: function () {} }); + var _dest = Writable({ write: function () {} }); + var _dest2 = Writable({ write: function () {} }); + + var destCount = 0; + var srcCheckEventNames = ['end', 'data']; + var destCheckEventNames = ['close', 'finish', 'drain', 'error', 'unpipe']; + + var checkSrcCleanup = common.mustCall(function () { + assert.strictEqual(_source._readableState.pipes, null); + assert.strictEqual(_source._readableState.pipesCount, 0); + assert.strictEqual(_source._readableState.flowing, false); + + srcCheckEventNames.forEach(function (eventName) { + assert.strictEqual(_source.listenerCount(eventName), 0, 'source\'s \'' + eventName + '\' event listeners not removed'); + }); + }); + + function checkDestCleanup(dest) { + var currentDestId = ++destCount; + _source.pipe(dest); + + var unpipeChecker = common.mustCall(function () { + assert.deepStrictEqual(dest.listeners('unpipe'), [unpipeChecker], 'destination{' + currentDestId + '} should have a \'unpipe\' event ' + 'listener which is `unpipeChecker`'); + dest.removeListener('unpipe', unpipeChecker); + destCheckEventNames.forEach(function (eventName) { + assert.strictEqual(dest.listenerCount(eventName), 0, 'destination{' + currentDestId + '}\'s \'' + eventName + '\' event ' + 'listeners not removed'); + }); + + if (--destCount === 0) checkSrcCleanup(); + }); + + dest.on('unpipe', unpipeChecker); + } + + checkDestCleanup(_dest); + checkDestCleanup(_dest2); + _source.unpipe(); +} \ No newline at end of file diff --git a/test/parallel/test-stream-transform-final-sync.js b/test/parallel/test-stream-transform-final-sync.js index 8667612bca..ad9d265080 100644 --- a/test/parallel/test-stream-transform-final-sync.js +++ b/test/parallel/test-stream-transform-final-sync.js @@ -9,7 +9,7 @@ var state = 0; /* What you do -var stream = new tream.Transform({ +var stream = new stream.Transform({ transform: function transformCallback(chunk, _, next) { // part 1 this.push(chunk); diff --git a/test/parallel/test-stream-transform-final.js b/test/parallel/test-stream-transform-final.js index 50e0ce8a1d..2f16312951 100644 --- a/test/parallel/test-stream-transform-final.js +++ b/test/parallel/test-stream-transform-final.js @@ -9,7 +9,7 @@ var state = 0; /* What you do -var stream = new tream.Transform({ +var stream = new stream.Transform({ transform: function transformCallback(chunk, _, next) { // part 1 this.push(chunk); diff --git a/test/parallel/test-stream-writable-destroy.js b/test/parallel/test-stream-writable-destroy.js index 3aeff6c445..c00873bfb5 100644 --- a/test/parallel/test-stream-writable-destroy.js +++ b/test/parallel/test-stream-writable-destroy.js @@ -172,6 +172,32 @@ var _require2 = require('util'), assert.strictEqual(_write7.destroyed, true); } +{ + var writable = new Writable({ + destroy: common.mustCall(function (err, cb) { + process.nextTick(cb, new Error('kaboom 1')); + }), + write: function (chunk, enc, cb) { + cb(); + } + }); + + writable.on('close', common.mustNotCall()); + writable.on('error', common.expectsError({ + type: Error, + message: 'kaboom 2' + })); + + writable.destroy(); + assert.strictEqual(writable.destroyed, true); + assert.strictEqual(writable._writableState.errorEmitted, false); + + // Test case where `writable.destroy()` is called again with an error before + // the `_destroy()` callback is called. + writable.destroy(new Error('kaboom 2')); + assert.strictEqual(writable._writableState.errorEmitted, true); +} + { var _write8 = new Writable({ write: function (chunk, enc, cb) { @@ -214,4 +240,20 @@ var _require2 = require('util'), _write9.destroy(_expected4, common.mustCall(function (err) { assert.strictEqual(_expected4, err); })); +} + +{ + // Checks that `._undestroy()` restores the state so that `final` will be + // called again. + var _write10 = new Writable({ + write: common.mustNotCall(), + final: common.mustCall(function (cb) { + return cb(); + }, 2) + }); + + _write10.end(); + _write10.destroy(); + _write10._undestroy(); + _write10.end(); } \ No newline at end of file diff --git a/test/parallel/test-stream2-transform.js b/test/parallel/test-stream2-transform.js index 62aa1d2bdb..d155c52b1f 100644 --- a/test/parallel/test-stream2-transform.js +++ b/test/parallel/test-stream2-transform.js @@ -177,7 +177,7 @@ var Transform = require('../../lib/_stream_transform'); } { - // Verify assymetric transform (expand) + // Verify asymmetric transform (expand) var _pt7 = new Transform(); // emit each chunk 2 times. @@ -209,7 +209,7 @@ var Transform = require('../../lib/_stream_transform'); } { - // Verify assymetric trasform (compress) + // Verify asymmetric transform (compress) var _pt8 = new Transform(); // each output is the first char of 3 consecutive chunks, @@ -265,7 +265,7 @@ var Transform = require('../../lib/_stream_transform'); // this tests for a stall when data is written to a full stream // that has empty transforms. { - // Verify compex transform behavior + // Verify complex transform behavior var count = 0; var saved = null; var _pt9 = new Transform({ highWaterMark: 3 }); diff --git a/test/parallel/test-stream3-cork-uncork.js b/test/parallel/test-stream3-cork-uncork.js index d80e7645e1..50b2784679 100644 --- a/test/parallel/test-stream3-cork-uncork.js +++ b/test/parallel/test-stream3-cork-uncork.js @@ -67,7 +67,7 @@ writeChunks(inputChunks, function () { // trigger writing out the buffer w.uncork(); - // buffered bytes shoud be seen in current tick + // buffered bytes should be seen in current tick assert.strictEqual(seenChunks.length, 4); // did the chunks match