From 41080724571074d19f4975cc7748708ef42a040c Mon Sep 17 00:00:00 2001 From: Nikolai Vavilov Date: Mon, 23 Oct 2017 19:37:03 +0300 Subject: [PATCH 01/22] build,win: set /MP separately in Debug and Release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Setting /MP globally causes it to appear twice in the command line due to a GYP bug, which causes the project to be rebuilt unconditionally due to an msbuild bug. PR-URL: https://github.com/nodejs/node/pull/16415 Fixes: https://github.com/nodejs/node/issues/16367 Reviewed-By: Anna Henningsen Reviewed-By: James M Snell Reviewed-By: Colin Ihrig Reviewed-By: Minwoo Jung Reviewed-By: Tobias Nießen Reviewed-By: Refael Ackermann --- common.gypi | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/common.gypi b/common.gypi index 10adc556549..c55c84f5cca 100644 --- a/common.gypi +++ b/common.gypi @@ -120,6 +120,7 @@ 'BasicRuntimeChecks': 3, # /RTC1 'AdditionalOptions': [ '/bigobj', # prevent error C1128 in VS2015 + '/MP', # compile across multiple CPUs ], }, 'VCLinkerTool': { @@ -175,6 +176,9 @@ 'EnableFunctionLevelLinking': 'true', 'EnableIntrinsicFunctions': 'true', 'RuntimeTypeInfo': 'false', + 'AdditionalOptions': [ + '/MP', # compile across multiple CPUs + ], }, 'VCLibrarianTool': { 'AdditionalOptions': [ @@ -207,9 +211,6 @@ # and their sheer number drowns out other, more legitimate warnings. 'DisableSpecificWarnings': ['4267'], 'WarnAsError': 'false', - 'AdditionalOptions': [ - '/MP', # compile across multiple CPUs - ], }, 'VCLibrarianTool': { }, From 9aa31bccb4e3ee16d4dc908c083f3d0f9d0e7801 Mon Sep 17 00:00:00 2001 From: Myles Borins Date: Mon, 23 Oct 2017 18:35:14 -0400 Subject: [PATCH 02/22] 2017-10-24, Node.js Version 8.8.0 (Current) Notable Changes: * crypto: - expose ECDH class https://github.com/nodejs/node/pull/8188 * http2: - http2 is now exposed by defualt without the need for a flag https://github.com/nodejs/node/pull/15685 - a new environment varible NODE\_NO\_HTTP2 has been added to allow userland http2 to be required https://github.com/nodejs/node/pull/15685 - support has been added for generic `Duplex` streams https://github.com/nodejs/node/pull/16269 * module: - resolve and instantiate loader pipeline hooks have been added to the ESM lifecycle https://github.com/nodejs/node/pull/15445 * zlib: - CVE-2017-14919 - In zlib v1.2.9, a change was made that causes an error to be raised when a raw deflate stream is initialized with windowBits set to 8. On some versions this crashes Node and you cannot recover from it, while on some versions it throws an exception. Node.js will now gracefully set windowBits to 9 replicating the legacy behavior to avoid a DOS vector. https://github.com/nodejs-private/node-private/pull/95 PR-URL: https://github.com/nodejs-private/node-private/pull/98 --- CHANGELOG.md | 3 +- doc/api/child_process.md | 12 +- doc/api/domain.md | 3 +- doc/changelogs/CHANGELOG_V8.md | 312 ++++++++++++++++++++++++++++++++- 4 files changed, 320 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d7e2c904b2..83660f2476a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,8 @@ release. -8.7.0
+8.8.0
+8.7.0
8.6.0
8.5.0
8.4.0
diff --git a/doc/api/child_process.md b/doc/api/child_process.md index 413949c287d..64bee59a661 100644 --- a/doc/api/child_process.md +++ b/doc/api/child_process.md @@ -128,7 +128,7 @@ exec('"my script.cmd" a b', (err, stdout, stderr) => { @@ -244,7 +244,7 @@ lsExample(); @@ -375,7 +375,7 @@ supported by `child_process.fork()` and will be ignored if set. +* `request` {string} The module path to resolve. +* `options` {Object} + * `paths` {Array} Paths to resolve module location from. If present, these + paths are used instead of the default resolution paths. Note that each of + these paths is used as a starting point for the module resolution algorithm, + meaning that the `node_modules` hierarchy is checked from this location. +* Returns: {string} + Use the internal `require()` machinery to look up the location of a module, but rather than loading the module, just return the resolved filename. +#### require.resolve.paths(request) + + +* `request` {string} The module path whose lookup paths are being retrieved. +* Returns: {Array} + +Returns an array containing the paths searched during resolution of `request`. + ## The `module` Object -* {net.Socket} +* {net.Socket|tls.TLSSocket} -Returns a Proxy object that acts as a `net.Socket` but applies getters, -setters and methods based on HTTP/2 logic. +Returns a Proxy object that acts as a `net.Socket` (or `tls.TLSSocket`) but +applies getters, setters and methods based on HTTP/2 logic. `destroyed`, `readable`, and `writable` properties will be retrieved from and set on `request.stream`. @@ -2293,7 +2297,7 @@ will result in a [`TypeError`][] being thrown. added: v8.4.0 --> -* {net.Socket} +* {net.Socket|tls.TLSSocket} See [`response.socket`][]. @@ -2510,10 +2514,10 @@ Returns `response`. added: v8.4.0 --> -* {net.Socket} +* {net.Socket|tls.TLSSocket} -Returns a Proxy object that acts as a `net.Socket` but applies getters, -setters and methods based on HTTP/2 logic. +Returns a Proxy object that acts as a `net.Socket` (or `tls.TLSSocket`) but +applies getters, setters and methods based on HTTP/2 logic. `destroyed`, `readable`, and `writable` properties will be retrieved from and set on `response.stream`. diff --git a/lib/internal/errors.js b/lib/internal/errors.js index a719570841e..cc787b6061d 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -208,8 +208,7 @@ E('ERR_HTTP2_INVALID_STREAM', 'The stream has been destroyed'); E('ERR_HTTP2_MAX_PENDING_SETTINGS_ACK', (max) => `Maximum number of pending settings acknowledgements (${max})`); E('ERR_HTTP2_NO_SOCKET_MANIPULATION', - 'HTTP/2 sockets should not be directly read from, written to, ' + - 'paused and/or resumed.'); + 'HTTP/2 sockets should not be directly manipulated (e.g. read and written)'); E('ERR_HTTP2_OUT_OF_STREAMS', 'No stream ID is available because maximum stream ID has been reached'); E('ERR_HTTP2_PAYLOAD_FORBIDDEN', diff --git a/lib/internal/http2/compat.js b/lib/internal/http2/compat.js index 84f15b2ed8a..c96fc931969 100644 --- a/lib/internal/http2/compat.js +++ b/lib/internal/http2/compat.js @@ -5,6 +5,7 @@ const Readable = Stream.Readable; const binding = process.binding('http2'); const constants = binding.constants; const errors = require('internal/errors'); +const { kSocket } = require('internal/http2/util'); const kFinish = Symbol('finish'); const kBeginSend = Symbol('begin-send'); @@ -176,15 +177,15 @@ const proxySocketHandler = { throw new errors.Error('ERR_HTTP2_NO_SOCKET_MANIPULATION'); default: const ref = stream.session !== undefined ? - stream.session.socket : stream; + stream.session[kSocket] : stream; const value = ref[prop]; return typeof value === 'function' ? value.bind(ref) : value; } }, getPrototypeOf(stream) { if (stream.session !== undefined) - return stream.session.socket.constructor.prototype; - return stream.prototype; + return Reflect.getPrototypeOf(stream.session[kSocket]); + return Reflect.getPrototypeOf(stream); }, set(stream, prop, value) { switch (prop) { @@ -201,9 +202,9 @@ const proxySocketHandler = { case 'setTimeout': const session = stream.session; if (session !== undefined) - session[prop] = value; + session.setTimeout = value; else - stream[prop] = value; + stream.setTimeout = value; return true; case 'write': case 'read': @@ -212,7 +213,7 @@ const proxySocketHandler = { throw new errors.Error('ERR_HTTP2_NO_SOCKET_MANIPULATION'); default: const ref = stream.session !== undefined ? - stream.session.socket : stream; + stream.session[kSocket] : stream; ref[prop] = value; return true; } diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index 8d8f200262d..727ca517989 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -38,6 +38,7 @@ const { getSettings, getStreamState, isPayloadMeaningless, + kSocket, mapToHeaders, NghttpError, sessionName, @@ -70,10 +71,10 @@ const kOptions = Symbol('options'); const kOwner = Symbol('owner'); const kProceed = Symbol('proceed'); const kProtocol = Symbol('protocol'); +const kProxySocket = Symbol('proxy-socket'); const kRemoteSettings = Symbol('remote-settings'); const kServer = Symbol('server'); const kSession = Symbol('session'); -const kSocket = Symbol('socket'); const kState = Symbol('state'); const kType = Symbol('type'); @@ -672,6 +673,48 @@ function finishSessionDestroy(self, socket) { debug(`[${sessionName(self[kType])}] nghttp2session destroyed`); } +const proxySocketHandler = { + get(session, prop) { + switch (prop) { + case 'setTimeout': + return session.setTimeout.bind(session); + case 'destroy': + case 'emit': + case 'end': + case 'pause': + case 'read': + case 'resume': + case 'write': + throw new errors.Error('ERR_HTTP2_NO_SOCKET_MANIPULATION'); + default: + const socket = session[kSocket]; + const value = socket[prop]; + return typeof value === 'function' ? value.bind(socket) : value; + } + }, + getPrototypeOf(session) { + return Reflect.getPrototypeOf(session[kSocket]); + }, + set(session, prop, value) { + switch (prop) { + case 'setTimeout': + session.setTimeout = value; + return true; + case 'destroy': + case 'emit': + case 'end': + case 'pause': + case 'read': + case 'resume': + case 'write': + throw new errors.Error('ERR_HTTP2_NO_SOCKET_MANIPULATION'); + default: + session[kSocket][prop] = value; + return true; + } + } +}; + // Upon creation, the Http2Session takes ownership of the socket. The session // may not be ready to use immediately if the socket is not yet fully connected. class Http2Session extends EventEmitter { @@ -707,6 +750,7 @@ class Http2Session extends EventEmitter { }; this[kType] = type; + this[kProxySocket] = null; this[kSocket] = socket; // Do not use nagle's algorithm @@ -756,7 +800,10 @@ class Http2Session extends EventEmitter { // The socket owned by this session get socket() { - return this[kSocket]; + const proxySocket = this[kProxySocket]; + if (proxySocket === null) + return this[kProxySocket] = new Proxy(this, proxySocketHandler); + return proxySocket; } // The session type @@ -957,6 +1004,7 @@ class Http2Session extends EventEmitter { // Disassociate from the socket and server const socket = this[kSocket]; // socket.pause(); + delete this[kProxySocket]; delete this[kSocket]; delete this[kServer]; @@ -2155,30 +2203,6 @@ function socketDestroy(error) { this.destroy(error); } -function socketOnResume() { - if (this._paused) - return this.pause(); - if (this._handle && !this._handle.reading) { - this._handle.reading = true; - this._handle.readStart(); - } -} - -function socketOnPause() { - if (this._handle && this._handle.reading) { - this._handle.reading = false; - this._handle.readStop(); - } -} - -function socketOnDrain() { - const needPause = 0 > this._writableState.highWaterMark; - if (this._paused && !needPause) { - this._paused = false; - this.resume(); - } -} - // When an Http2Session emits an error, first try to forward it to the // server as a sessionError; failing that, forward it to the socket as // a sessionError; failing that, destroy, remove the error listener, and @@ -2267,9 +2291,6 @@ function connectionListener(socket) { } socket.on('error', socketOnError); - socket.on('resume', socketOnResume); - socket.on('pause', socketOnPause); - socket.on('drain', socketOnDrain); socket.on('close', socketOnClose); // Set up the Session @@ -2426,9 +2447,6 @@ function connect(authority, options, listener) { } socket.on('error', socketOnError); - socket.on('resume', socketOnResume); - socket.on('pause', socketOnPause); - socket.on('drain', socketOnDrain); socket.on('close', socketOnClose); const session = new ClientHttp2Session(options, socket); diff --git a/lib/internal/http2/util.js b/lib/internal/http2/util.js index 4610be7e4d3..2426a409cfc 100644 --- a/lib/internal/http2/util.js +++ b/lib/internal/http2/util.js @@ -3,6 +3,8 @@ const binding = process.binding('http2'); const errors = require('internal/errors'); +const kSocket = Symbol('socket'); + const { NGHTTP2_SESSION_CLIENT, NGHTTP2_SESSION_SERVER, @@ -551,6 +553,7 @@ module.exports = { getSettings, getStreamState, isPayloadMeaningless, + kSocket, mapToHeaders, NghttpError, sessionName, diff --git a/test/parallel/test-http2-client-destroy.js b/test/parallel/test-http2-client-destroy.js index f10bf60ce34..8b91f2d2104 100644 --- a/test/parallel/test-http2-client-destroy.js +++ b/test/parallel/test-http2-client-destroy.js @@ -1,3 +1,5 @@ +// Flags: --expose-internals + 'use strict'; const common = require('../common'); @@ -5,6 +7,7 @@ if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); const h2 = require('http2'); +const { kSocket } = require('internal/http2/util'); { const server = h2.createServer(); @@ -13,7 +16,7 @@ const h2 = require('http2'); common.mustCall(() => { const destroyCallbacks = [ (client) => client.destroy(), - (client) => client.socket.destroy() + (client) => client[kSocket].destroy() ]; let remaining = destroyCallbacks.length; @@ -23,9 +26,9 @@ const h2 = require('http2'); client.on( 'connect', common.mustCall(() => { - const socket = client.socket; + const socket = client[kSocket]; - assert(client.socket, 'client session has associated socket'); + assert(socket, 'client session has associated socket'); assert( !client.destroyed, 'client has not been destroyed before destroy is called' @@ -41,7 +44,7 @@ const h2 = require('http2'); destroyCallback(client); assert( - !client.socket, + !client[kSocket], 'client.socket undefined after destroy is called' ); diff --git a/test/parallel/test-http2-client-socket-destroy.js b/test/parallel/test-http2-client-socket-destroy.js index f56a45cc3b5..faf4643b030 100644 --- a/test/parallel/test-http2-client-socket-destroy.js +++ b/test/parallel/test-http2-client-socket-destroy.js @@ -1,9 +1,13 @@ +// Flags: --expose-internals + 'use strict'; const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); const h2 = require('http2'); +const { kSocket } = require('internal/http2/util'); + const body = '

this is some data

'; @@ -32,7 +36,7 @@ server.on('listening', common.mustCall(function() { req.on('response', common.mustCall(() => { // send a premature socket close - client.socket.destroy(); + client[kSocket].destroy(); })); req.on('data', common.mustNotCall()); diff --git a/test/parallel/test-http2-compat-socket-set.js b/test/parallel/test-http2-compat-socket-set.js index 7411fef6026..87b7a2b4448 100644 --- a/test/parallel/test-http2-compat-socket-set.js +++ b/test/parallel/test-http2-compat-socket-set.js @@ -12,8 +12,8 @@ const h2 = require('http2'); const errMsg = { code: 'ERR_HTTP2_NO_SOCKET_MANIPULATION', type: Error, - message: 'HTTP/2 sockets should not be directly read from, written to, ' + - 'paused and/or resumed.' + message: 'HTTP/2 sockets should not be directly manipulated ' + + '(e.g. read and written)' }; const server = h2.createServer(); diff --git a/test/parallel/test-http2-compat-socket.js b/test/parallel/test-http2-compat-socket.js index 8292232f74e..aed74149f72 100644 --- a/test/parallel/test-http2-compat-socket.js +++ b/test/parallel/test-http2-compat-socket.js @@ -14,8 +14,8 @@ const net = require('net'); const errMsg = { code: 'ERR_HTTP2_NO_SOCKET_MANIPULATION', type: Error, - message: 'HTTP/2 sockets should not be directly read from, written to, ' + - 'paused and/or resumed.' + message: 'HTTP/2 sockets should not be directly manipulated ' + + '(e.g. read and written)' }; const server = h2.createServer(); diff --git a/test/parallel/test-http2-create-client-secure-session.js b/test/parallel/test-http2-create-client-secure-session.js index 7447d4e5c63..62a79148dca 100644 --- a/test/parallel/test-http2-create-client-secure-session.js +++ b/test/parallel/test-http2-create-client-secure-session.js @@ -1,3 +1,5 @@ +// Flags: --expose-internals + 'use strict'; const common = require('../common'); @@ -8,6 +10,7 @@ if (!common.hasCrypto) const assert = require('assert'); const fixtures = require('../common/fixtures'); const h2 = require('http2'); +const { kSocket } = require('internal/http2/util'); const tls = require('tls'); function loadKey(keyname) { @@ -15,7 +18,7 @@ function loadKey(keyname) { } function onStream(stream, headers) { - const socket = stream.session.socket; + const socket = stream.session[kSocket]; assert(headers[':authority'].startsWith(socket.servername)); stream.respond({ 'content-type': 'text/html', @@ -55,7 +58,7 @@ function verifySecureSession(key, cert, ca, opts) { assert.strictEqual(jsonData.servername, opts.servername || 'localhost'); assert.strictEqual(jsonData.alpnProtocol, 'h2'); server.close(); - client.socket.destroy(); + client[kSocket].destroy(); })); req.end(); }); diff --git a/test/parallel/test-http2-server-socket-destroy.js b/test/parallel/test-http2-server-socket-destroy.js index 0218f7357ae..8291c415284 100644 --- a/test/parallel/test-http2-server-socket-destroy.js +++ b/test/parallel/test-http2-server-socket-destroy.js @@ -1,10 +1,13 @@ +// Flags: --expose-internals + 'use strict'; const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); -const h2 = require('http2'); const assert = require('assert'); +const h2 = require('http2'); +const { kSocket } = require('internal/http2/util'); const { HTTP2_HEADER_METHOD, @@ -24,7 +27,7 @@ function onStream(stream) { }); stream.write('test'); - const socket = stream.session.socket; + const socket = stream.session[kSocket]; // When the socket is destroyed, the close events must be triggered // on the socket, server and session. diff --git a/test/parallel/test-http2-server-socketerror.js b/test/parallel/test-http2-server-socketerror.js index cad92fd2996..24945e531af 100644 --- a/test/parallel/test-http2-server-socketerror.js +++ b/test/parallel/test-http2-server-socketerror.js @@ -1,3 +1,5 @@ +// Flags: --expose-internals + 'use strict'; const common = require('../common'); @@ -5,6 +7,7 @@ if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); const http2 = require('http2'); +const { kSocket } = require('internal/http2/util'); const server = http2.createServer(); server.on('stream', common.mustCall((stream) => { @@ -19,12 +22,12 @@ server.on('session', common.mustCall((session) => { type: Error, message: 'test' })(error); - assert.strictEqual(socket, session.socket); + assert.strictEqual(socket, session[kSocket]); }); const isNotCalled = common.mustNotCall(); session.on('socketError', handler); server.on('socketError', isNotCalled); - session.socket.emit('error', new Error('test')); + session[kSocket].emit('error', new Error('test')); session.removeListener('socketError', handler); server.removeListener('socketError', isNotCalled); @@ -35,10 +38,10 @@ server.on('session', common.mustCall((session) => { type: Error, message: 'test' })(error); - assert.strictEqual(socket, session.socket); + assert.strictEqual(socket, session[kSocket]); assert.strictEqual(session, session); })); - session.socket.emit('error', new Error('test')); + session[kSocket].emit('error', new Error('test')); })); server.listen(0, common.mustCall(() => { diff --git a/test/parallel/test-http2-socket-proxy.js b/test/parallel/test-http2-socket-proxy.js new file mode 100644 index 00000000000..60f31837790 --- /dev/null +++ b/test/parallel/test-http2-socket-proxy.js @@ -0,0 +1,88 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); +const net = require('net'); + +// Tests behaviour of the proxied socket on Http2Session + +const errMsg = { + code: 'ERR_HTTP2_NO_SOCKET_MANIPULATION', + type: Error, + message: 'HTTP/2 sockets should not be directly manipulated ' + + '(e.g. read and written)' +}; + +const server = h2.createServer(); + +server.on('stream', common.mustCall(function(stream, headers) { + const socket = stream.session.socket; + const session = stream.session; + + assert.ok(socket instanceof net.Socket); + + assert.strictEqual(socket.writable, true); + assert.strictEqual(socket.readable, true); + assert.strictEqual(typeof socket.address(), 'object'); + + socket.setTimeout(987); + assert.strictEqual(session._idleTimeout, 987); + + common.expectsError(() => socket.destroy, errMsg); + common.expectsError(() => socket.emit, errMsg); + common.expectsError(() => socket.end, errMsg); + common.expectsError(() => socket.pause, errMsg); + common.expectsError(() => socket.read, errMsg); + common.expectsError(() => socket.resume, errMsg); + common.expectsError(() => socket.write, errMsg); + + common.expectsError(() => (socket.destroy = undefined), errMsg); + common.expectsError(() => (socket.emit = undefined), errMsg); + common.expectsError(() => (socket.end = undefined), errMsg); + common.expectsError(() => (socket.pause = undefined), errMsg); + common.expectsError(() => (socket.read = undefined), errMsg); + common.expectsError(() => (socket.resume = undefined), errMsg); + common.expectsError(() => (socket.write = undefined), errMsg); + + assert.doesNotThrow(() => (socket.on = socket.on)); + assert.doesNotThrow(() => (socket.once = socket.once)); + + stream.respond(); + + socket.writable = 0; + socket.readable = 0; + assert.strictEqual(socket.writable, 0); + assert.strictEqual(socket.readable, 0); + + stream.session.destroy(); + + socket.setTimeout = undefined; + assert.strictEqual(session.setTimeout, undefined); + + stream.session.on('close', common.mustCall(() => { + assert.strictEqual(session.socket, undefined); + })); +})); + +server.listen(0, common.mustCall(function() { + const port = server.address().port; + const url = `http://localhost:${port}`; + const client = h2.connect(url, common.mustCall(() => { + const headers = { + ':path': '/', + ':method': 'GET', + ':scheme': 'http', + ':authority': `localhost:${port}` + }; + const request = client.request(headers); + request.on('end', common.mustCall(() => { + client.destroy(); + server.close(); + })); + request.end(); + request.resume(); + })); +})); From 3621889c800707f3ca720c8de5f05a798a1fe5a8 Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Sat, 21 Oct 2017 12:04:35 -0700 Subject: [PATCH 19/22] doc: improve http2 documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Provide section headings for server-side and client side examples. Add error handling and TLS to server-side example, following example of `https`. Add error handling, TLS, more efficient Buffer usage, and header printing to client example. PR-URL: https://github.com/nodejs/node/pull/16366 Fixes: https://github.com/nodejs/node/issues/16345 Reviewed-By: Anatoli Papirovski Reviewed-By: Anna Henningsen Reviewed-By: James M Snell Reviewed-By: Tobias Nießen --- doc/api/http2.md | 55 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/doc/api/http2.md b/doc/api/http2.md index abf0ac5b522..1272b920522 100644 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -16,14 +16,25 @@ support for HTTP/2 protocol features. It is specifically *not* designed for compatibility with the existing [HTTP/1][] module API. However, the [Compatibility API][] is. +The `http2` Core API is much more symmetric between client and server than the +`http` API. For instance, most events, like `error` and `socketError`, can be +emitted either by client-side code or server-side code. + +### Server-side example + The following illustrates a simple, plain-text HTTP/2 server using the Core API: ```js const http2 = require('http2'); +const fs = require('fs'); -// Create a plain-text HTTP/2 server -const server = http2.createServer(); +const server = http2.createSecureServer({ + key: fs.readFileSync('localhost-privkey.pem'), + cert: fs.readFileSync('localhost-cert.pem') +}); +server.on('error', (err) => console.error(err)); +server.on('socketError', (err) => console.error(err)); server.on('stream', (stream, headers) => { // stream is a Duplex @@ -34,34 +45,44 @@ server.on('stream', (stream, headers) => { stream.end('

Hello World

'); }); -server.listen(80); +server.listen(8443); ``` -Note that the above example is an HTTP/2 server that does not support SSL. -This is significant as most browsers support HTTP/2 only with SSL. -To make the above server be able to serve content to browsers, -replace `http2.createServer()` with -`http2.createSecureServer({key: /* your SSL key */, cert: /* your SSL cert */})`. +To generate the certificate and key for this example, run: + +```bash +openssl req -x509 -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' \ + -keyout localhost-privkey.pem -out localhost-cert.pem +``` + +### Client-side example The following illustrates an HTTP/2 client: ```js const http2 = require('http2'); +const fs = require('fs'); +const client = http2.connect('https://localhost:8443', { + ca: fs.readFileSync('localhost-cert.pem') +}); +client.on('socketError', (err) => console.error(err)); +client.on('error', (err) => console.error(err)); -const client = http2.connect('http://localhost:80'); - -// req is a Duplex const req = client.request({ ':path': '/' }); -req.on('response', (headers) => { - console.log(headers[':status']); - console.log(headers['date']); +req.on('response', (headers, flags) => { + for (const name in headers) { + console.log(`${name}: ${headers[name]}`); + } }); -let data = ''; req.setEncoding('utf8'); -req.on('data', (d) => data += d); -req.on('end', () => client.destroy()); +let data = ''; +req.on('data', (chunk) => { data += chunk; }); +req.on('end', () => { + console.log(`\n${data}`); + client.destroy(); +}); req.end(); ``` From 8172f4547e7e2a7e1b7363e1cb9d7431fad241ff Mon Sep 17 00:00:00 2001 From: Bryan English Date: Sat, 21 Oct 2017 23:25:59 -0700 Subject: [PATCH 20/22] buffer: move setupBufferJS to internal Stashing it away in internal/buffer so that it can't be used in userland, but can still be used in internals. PR-URL: https://github.com/nodejs/node/pull/16391 Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Timothy Gu Reviewed-By: Refael Ackermann Reviewed-By: James M Snell --- lib/buffer.js | 3 ++- lib/internal/bootstrap_node.js | 4 ++++ lib/internal/buffer.js | 13 +++++++++-- .../test-buffer-bindingobj-no-zerofill.js | 22 ++++++++----------- 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/lib/buffer.js b/lib/buffer.js index 908e8b56e81..f326aef751a 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -35,7 +35,6 @@ const { readDoubleLE: _readDoubleLE, readFloatBE: _readFloatBE, readFloatLE: _readFloatLE, - setupBufferJS, swap16: _swap16, swap32: _swap32, swap64: _swap64, @@ -63,6 +62,8 @@ const errors = require('internal/errors'); const internalBuffer = require('internal/buffer'); +const { setupBufferJS } = internalBuffer; + const bindingObj = {}; class FastBuffer extends Uint8Array { diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index 366eff06f0d..9569d205ed7 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -292,6 +292,10 @@ } }); + // This, as side effect, removes `setupBufferJS` from the buffer binding, + // and exposes it on `internal/buffer`. + NativeModule.require('internal/buffer'); + global.Buffer = NativeModule.require('buffer').Buffer; process.domain = null; process._exiting = false; diff --git a/lib/internal/buffer.js b/lib/internal/buffer.js index 178a475715e..105ff4132a9 100644 --- a/lib/internal/buffer.js +++ b/lib/internal/buffer.js @@ -1,4 +1,13 @@ 'use strict'; -// This is needed still for FastBuffer -module.exports = {}; +const binding = process.binding('buffer'); +const { setupBufferJS } = binding; + +// Remove from the binding so that function is only available as exported here. +// (That is, for internal use only.) +delete binding.setupBufferJS; + +// FastBuffer wil be inserted here by lib/buffer.js +module.exports = { + setupBufferJS +}; diff --git a/test/parallel/test-buffer-bindingobj-no-zerofill.js b/test/parallel/test-buffer-bindingobj-no-zerofill.js index be89c99d67b..ab584c2597f 100644 --- a/test/parallel/test-buffer-bindingobj-no-zerofill.js +++ b/test/parallel/test-buffer-bindingobj-no-zerofill.js @@ -11,13 +11,11 @@ const assert = require('assert'); const buffer = require('buffer'); // Monkey-patch setupBufferJS() to have an undefined zeroFill. -const process = require('process'); -const originalBinding = process.binding; +const internalBuffer = require('internal/buffer'); -const binding = originalBinding('buffer'); -const originalSetup = binding.setupBufferJS; +const originalSetup = internalBuffer.setupBufferJS; -binding.setupBufferJS = (proto, obj) => { +internalBuffer.setupBufferJS = (proto, obj) => { originalSetup(proto, obj); assert.strictEqual(obj.zeroFill[0], 1); delete obj.zeroFill; @@ -25,19 +23,14 @@ binding.setupBufferJS = (proto, obj) => { const bindingObj = {}; -binding.setupBufferJS(Buffer.prototype, bindingObj); +internalBuffer.setupBufferJS(Buffer.prototype, bindingObj); assert.strictEqual(bindingObj.zeroFill, undefined); -process.binding = (bindee) => { - if (bindee === 'buffer') - return binding; - return originalBinding(bindee); -}; - // Load from file system because internal buffer is already loaded and we're // testing code that runs on first load only. // Do not move this require() to top of file. It is important that -// `process.binding('buffer').setupBufferJS` be monkey-patched before this runs. +// `require('internal/buffer').setupBufferJS` be monkey-patched before this +// runs. const monkeyPatchedBuffer = require('../../lib/buffer'); // On unpatched buffer, allocUnsafe() should not zero fill memory. It's always @@ -51,3 +44,6 @@ while (uninitialized.every((val) => val === 0)) // zero-fill in that case. const zeroFilled = monkeyPatchedBuffer.Buffer.allocUnsafe(1024); assert(zeroFilled.every((val) => val === 0)); + +// setupBufferJS shouldn't still be exposed on the binding +assert(!('setupBufferJs' in process.binding('buffer'))); From 8a00f1d129ac5fcbbce8e115a2576e0ea080e4ed Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Wed, 25 Oct 2017 12:36:24 +0200 Subject: [PATCH 21/22] test: call toLowerCase on the resolved module The commit updates test-require-resolve.js to call toLowerCase on the resolved module instead of the path. Currently this test will fail if the path to where node exists contains uppercase letters. For example: ``` $ out/Release/node test/parallel/test-require-resolve.js /root/rpmbuild/BUILD/node-v8.8.0/test/parallel module.js:515 throw err; ^ Error: Cannot find module '/root/rpmbuild/build/node-v8.8.0/test/fixtures/nested-index/one' at Function.Module._resolveFilename (module.js:513:15) at Function.resolve (internal/module.js:18:19) at Object. (/root/rpmbuild/BUILD/node-v8.8.0/test/parallel/test-require-resolve.js:37:11) at Module._compile (module.js:612:30) at Object.Module._extensions..js (module.js:623:10) at Module.load (module.js:531:32) at tryModuleLoad (module.js:494:12) at Function.Module._load (module.js:486:3) at Function.Module.runMain (module.js:653:10) at startup (bootstrap_node.js:187:16) ``` PR-URL: https://github.com/nodejs/node/pull/16486 Reviewed-By: Luigi Pinca > Reviewed-By: Ben Noordhuis > Reviewed-By: Colin Ihrig > Reviewed-By: Anna Henningsen > Reviewed-By: James M Snell > --- test/parallel/test-require-resolve.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/parallel/test-require-resolve.js b/test/parallel/test-require-resolve.js index c0595ba9161..6f2253a4e27 100644 --- a/test/parallel/test-require-resolve.js +++ b/test/parallel/test-require-resolve.js @@ -32,7 +32,7 @@ assert.strictEqual( require.resolve(fixtures.path('a')).toLowerCase()); assert.strictEqual( fixtures.path('nested-index', 'one', 'index.js').toLowerCase(), - require.resolve(fixtures.path('nested-index', 'one').toLowerCase())); + require.resolve(fixtures.path('nested-index', 'one')).toLowerCase()); assert.strictEqual('path', require.resolve('path')); // Test configurable resolve() paths. From d2e44d5e7fcc93c615ac5cef794bdfbcd32c3fc0 Mon Sep 17 00:00:00 2001 From: Teppei Sato Date: Wed, 25 Oct 2017 08:30:45 +0900 Subject: [PATCH 22/22] doc: remove loader hooks from unsupported features PR-URL: https://github.com/nodejs/node/pull/16465 Reviewed-By: James M Snell Reviewed-By: Gireesh Punathil Reviewed-By: Yuta Hiroto Reviewed-By: Franziska Hinkelmann Reviewed-By: Colin Ihrig --- doc/api/esm.md | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/api/esm.md b/doc/api/esm.md index 76592927dbd..d8143da378f 100644 --- a/doc/api/esm.md +++ b/doc/api/esm.md @@ -43,7 +43,6 @@ points into ESM graphs at run time. | `require('./foo.mjs')` | ES Modules have differing resolution and timing, use language standard `import()` | | `import()` | pending newer V8 release used in Node.js | | `import.meta` | pending V8 implementation | -| Loader Hooks | pending Node.js EP creation/consensus | ## Notable differences between `import` and `require`