aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorAnatoli Papirovski <apapirovski@mac.com>2017-10-02 21:56:53 -0400
committerMatteo Collina <hello@matteocollina.com>2017-10-06 14:04:22 -0700
commit2da7d9b8207d9c35d199734da75fa2bb15f326b6 (patch)
tree9aa1ed51d29bc3d9241539e9ded4d0e4da951970 /test
parent4f339b54e9cd8a2cb69b41d87832ad8ca3a6b5e2 (diff)
downloadandroid-node-v8-2da7d9b8207d9c35d199734da75fa2bb15f326b6.tar.gz
android-node-v8-2da7d9b8207d9c35d199734da75fa2bb15f326b6.tar.bz2
android-node-v8-2da7d9b8207d9c35d199734da75fa2bb15f326b6.zip
http2: near full http1 compatibility, add tests
Extensive re-work of http1 compatibility layer based on tests in express, on-finished and finalhandler. Fix handling of HEAD method to match http1. Adjust write, end, etc. to call writeHead as in http1 and as expected by user-land modules. Add socket proxy that instead uses the Http2Stream for the vast majority of socket interactions. Add and change tests to closer represent http1 behaviour. Refs: https://github.com/nodejs/node/pull/15633 Refs: https://github.com/expressjs/express/tree/master/test Refs: https://github.com/jshttp/on-finished/blob/master/test/test.js Refs: https://github.com/pillarjs/finalhandler/blob/master/test/test.js PR-URL: https://github.com/nodejs/node/pull/15702 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Diffstat (limited to 'test')
-rw-r--r--test/parallel/test-http2-compat-serverrequest-end.js9
-rw-r--r--test/parallel/test-http2-compat-serverrequest-headers.js21
-rw-r--r--test/parallel/test-http2-compat-serverrequest-pipe.js1
-rw-r--r--test/parallel/test-http2-compat-serverrequest.js7
-rw-r--r--test/parallel/test-http2-compat-serverresponse-destroy.js1
-rw-r--r--test/parallel/test-http2-compat-serverresponse-end.js191
-rw-r--r--test/parallel/test-http2-compat-serverresponse-finished.js14
-rw-r--r--test/parallel/test-http2-compat-serverresponse-flushheaders.js7
-rw-r--r--test/parallel/test-http2-compat-serverresponse-headers-after-destroy.js47
-rw-r--r--test/parallel/test-http2-compat-serverresponse-headers.js40
-rw-r--r--test/parallel/test-http2-compat-serverresponse-writehead.js2
-rw-r--r--test/parallel/test-http2-compat-socket-set.js107
-rw-r--r--test/parallel/test-http2-compat-socket.js89
-rw-r--r--test/parallel/test-http2-options-max-reserved-streams.js9
-rw-r--r--test/parallel/test-http2-server-rst-stream.js12
-rw-r--r--test/parallel/test-http2-stream-destroy-event-order.js28
16 files changed, 552 insertions, 33 deletions
diff --git a/test/parallel/test-http2-compat-serverrequest-end.js b/test/parallel/test-http2-compat-serverrequest-end.js
index 60a4876482..b6bfd04089 100644
--- a/test/parallel/test-http2-compat-serverrequest-end.js
+++ b/test/parallel/test-http2-compat-serverrequest-end.js
@@ -3,6 +3,7 @@
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
+const assert = require('assert');
const h2 = require('http2');
// Http2ServerRequest should always end readable stream
@@ -12,9 +13,17 @@ const server = h2.createServer();
server.listen(0, common.mustCall(function() {
const port = server.address().port;
server.once('request', common.mustCall(function(request, response) {
+ assert.strictEqual(request.complete, false);
request.on('data', () => {});
request.on('end', common.mustCall(() => {
+ assert.strictEqual(request.complete, true);
response.on('finish', common.mustCall(function() {
+ // the following tests edge cases on request socket
+ // right after finished fires but before backing
+ // Http2Stream is destroyed
+ assert.strictEqual(request.socket.readable, request.stream.readable);
+ assert.strictEqual(request.socket.readable, false);
+
server.close();
}));
response.end();
diff --git a/test/parallel/test-http2-compat-serverrequest-headers.js b/test/parallel/test-http2-compat-serverrequest-headers.js
index 05e645a362..58cc52c64f 100644
--- a/test/parallel/test-http2-compat-serverrequest-headers.js
+++ b/test/parallel/test-http2-compat-serverrequest-headers.js
@@ -41,6 +41,27 @@ server.listen(0, common.mustCall(function() {
request.url = '/one';
assert.strictEqual(request.url, '/one');
+ // third-party plugins for packages like express use query params to
+ // change the request method
+ request.method = 'POST';
+ assert.strictEqual(request.method, 'POST');
+ common.expectsError(
+ () => request.method = ' ',
+ {
+ code: 'ERR_INVALID_ARG_TYPE',
+ type: TypeError,
+ message: 'The "method" argument must be of type string'
+ }
+ );
+ common.expectsError(
+ () => request.method = true,
+ {
+ code: 'ERR_INVALID_ARG_TYPE',
+ type: TypeError,
+ message: 'The "method" argument must be of type string'
+ }
+ );
+
response.on('finish', common.mustCall(function() {
server.close();
}));
diff --git a/test/parallel/test-http2-compat-serverrequest-pipe.js b/test/parallel/test-http2-compat-serverrequest-pipe.js
index a8ef688fd7..24cee1c460 100644
--- a/test/parallel/test-http2-compat-serverrequest-pipe.js
+++ b/test/parallel/test-http2-compat-serverrequest-pipe.js
@@ -19,6 +19,7 @@ const server = http2.createServer();
server.on('request', common.mustCall((req, res) => {
const dest = req.pipe(fs.createWriteStream(fn));
dest.on('finish', common.mustCall(() => {
+ assert.strictEqual(req.complete, true);
assert.deepStrictEqual(fs.readFileSync(loc), fs.readFileSync(fn));
fs.unlinkSync(fn);
res.end();
diff --git a/test/parallel/test-http2-compat-serverrequest.js b/test/parallel/test-http2-compat-serverrequest.js
index 31cc5ff4ef..edcd7a8f8c 100644
--- a/test/parallel/test-http2-compat-serverrequest.js
+++ b/test/parallel/test-http2-compat-serverrequest.js
@@ -19,9 +19,6 @@ server.listen(0, common.mustCall(function() {
httpVersionMinor: 0
};
- assert.strictEqual(request.closed, false);
- assert.strictEqual(request.code, h2.constants.NGHTTP2_NO_ERROR);
-
assert.strictEqual(request.httpVersion, expected.version);
assert.strictEqual(request.httpVersionMajor, expected.httpVersionMajor);
assert.strictEqual(request.httpVersionMinor, expected.httpVersionMinor);
@@ -31,10 +28,8 @@ server.listen(0, common.mustCall(function() {
assert.strictEqual(request.socket, request.connection);
response.on('finish', common.mustCall(function() {
- assert.strictEqual(request.closed, true);
- assert.strictEqual(request.code, h2.constants.NGHTTP2_NO_ERROR);
process.nextTick(() => {
- assert.strictEqual(request.socket, undefined);
+ assert.ok(request.socket);
server.close();
});
}));
diff --git a/test/parallel/test-http2-compat-serverresponse-destroy.js b/test/parallel/test-http2-compat-serverresponse-destroy.js
index e8f648bf29..77e761b622 100644
--- a/test/parallel/test-http2-compat-serverresponse-destroy.js
+++ b/test/parallel/test-http2-compat-serverresponse-destroy.js
@@ -22,7 +22,6 @@ const server = http2.createServer(common.mustCall((req, res) => {
res.on('finish', common.mustCall(() => {
assert.doesNotThrow(() => res.destroy(nextError));
- assert.strictEqual(res.closed, true);
process.nextTick(() => {
assert.doesNotThrow(() => res.destroy(nextError));
});
diff --git a/test/parallel/test-http2-compat-serverresponse-end.js b/test/parallel/test-http2-compat-serverresponse-end.js
index 76cb692938..fafb3ea76d 100644
--- a/test/parallel/test-http2-compat-serverresponse-end.js
+++ b/test/parallel/test-http2-compat-serverresponse-end.js
@@ -1,6 +1,12 @@
'use strict';
-const { mustCall, mustNotCall, hasCrypto, skip } = require('../common');
+const {
+ mustCall,
+ mustNotCall,
+ hasCrypto,
+ platformTimeout,
+ skip
+} = require('../common');
if (!hasCrypto)
skip('missing crypto');
const { strictEqual } = require('assert');
@@ -18,15 +24,16 @@ const {
// It may be invoked repeatedly without throwing errors
// but callback will only be called once
const server = createServer(mustCall((request, response) => {
- strictEqual(response.closed, false);
response.end('end', 'utf8', mustCall(() => {
- strictEqual(response.closed, true);
response.end(mustNotCall());
process.nextTick(() => {
response.end(mustNotCall());
server.close();
});
}));
+ response.on('finish', mustCall(() => {
+ response.end(mustNotCall());
+ }));
response.end(mustNotCall());
}));
server.listen(0, mustCall(() => {
@@ -111,12 +118,77 @@ const {
}
{
- // Http2ServerResponse.end is not necessary on HEAD requests since the stream
- // is already closed. Headers, however, can still be sent to the client.
+ // Http2ServerResponse.end is necessary on HEAD requests in compat
+ // for http1 compatibility
const server = createServer(mustCall((request, response) => {
strictEqual(response.finished, true);
response.writeHead(HTTP_STATUS_OK, { foo: 'bar' });
- response.end(mustNotCall());
+ response.end('data', mustCall());
+ }));
+ server.listen(0, mustCall(() => {
+ const { port } = server.address();
+ const url = `http://localhost:${port}`;
+ const client = connect(url, mustCall(() => {
+ const headers = {
+ ':path': '/',
+ ':method': 'HEAD',
+ ':scheme': 'http',
+ ':authority': `localhost:${port}`
+ };
+ const request = client.request(headers);
+ request.on('response', mustCall((headers, flags) => {
+ strictEqual(headers[HTTP2_HEADER_STATUS], HTTP_STATUS_OK);
+ strictEqual(flags, 5); // the end of stream flag is set
+ strictEqual(headers.foo, 'bar');
+ }));
+ request.on('data', mustNotCall());
+ request.on('end', mustCall(() => {
+ client.destroy();
+ server.close();
+ }));
+ request.end();
+ request.resume();
+ }));
+ }));
+}
+
+{
+ // .end should trigger 'end' event on request if user did not attempt
+ // to read from the request
+ const server = createServer(mustCall((request, response) => {
+ request.on('end', mustCall());
+ response.end();
+ }));
+ server.listen(0, mustCall(() => {
+ const { port } = server.address();
+ const url = `http://localhost:${port}`;
+ const client = connect(url, mustCall(() => {
+ const headers = {
+ ':path': '/',
+ ':method': 'HEAD',
+ ':scheme': 'http',
+ ':authority': `localhost:${port}`
+ };
+ const request = client.request(headers);
+ request.on('data', mustNotCall());
+ request.on('end', mustCall(() => {
+ client.destroy();
+ server.close();
+ }));
+ request.end();
+ request.resume();
+ }));
+ }));
+}
+
+
+{
+ // Should be able to call .end with cb from stream 'streamClosed'
+ const server = createServer(mustCall((request, response) => {
+ response.writeHead(HTTP_STATUS_OK, { foo: 'bar' });
+ response.stream.on('streamClosed', mustCall(() => {
+ response.end(mustCall());
+ }));
}));
server.listen(0, mustCall(() => {
const { port } = server.address();
@@ -144,3 +216,110 @@ const {
}));
}));
}
+
+{
+ // Should be able to respond to HEAD request after timeout
+ const server = createServer(mustCall((request, response) => {
+ setTimeout(mustCall(() => {
+ response.writeHead(HTTP_STATUS_OK, { foo: 'bar' });
+ response.end('data', mustCall());
+ }), platformTimeout(10));
+ }));
+ server.listen(0, mustCall(() => {
+ const { port } = server.address();
+ const url = `http://localhost:${port}`;
+ const client = connect(url, mustCall(() => {
+ const headers = {
+ ':path': '/',
+ ':method': 'HEAD',
+ ':scheme': 'http',
+ ':authority': `localhost:${port}`
+ };
+ const request = client.request(headers);
+ request.on('response', mustCall((headers, flags) => {
+ strictEqual(headers[HTTP2_HEADER_STATUS], HTTP_STATUS_OK);
+ strictEqual(flags, 5); // the end of stream flag is set
+ strictEqual(headers.foo, 'bar');
+ }));
+ request.on('data', mustNotCall());
+ request.on('end', mustCall(() => {
+ client.destroy();
+ server.close();
+ }));
+ request.end();
+ request.resume();
+ }));
+ }));
+}
+
+{
+ // finish should only trigger after 'end' is called
+ const server = createServer(mustCall((request, response) => {
+ let finished = false;
+ response.writeHead(HTTP_STATUS_OK, { foo: 'bar' });
+ response.on('finish', mustCall(() => {
+ finished = false;
+ }));
+ response.end('data', mustCall(() => {
+ strictEqual(finished, false);
+ response.end('data', mustNotCall());
+ }));
+ }));
+ server.listen(0, mustCall(() => {
+ const { port } = server.address();
+ const url = `http://localhost:${port}`;
+ const client = connect(url, mustCall(() => {
+ const headers = {
+ ':path': '/',
+ ':method': 'HEAD',
+ ':scheme': 'http',
+ ':authority': `localhost:${port}`
+ };
+ const request = client.request(headers);
+ request.on('response', mustCall((headers, flags) => {
+ strictEqual(headers[HTTP2_HEADER_STATUS], HTTP_STATUS_OK);
+ strictEqual(flags, 5); // the end of stream flag is set
+ strictEqual(headers.foo, 'bar');
+ }));
+ request.on('data', mustNotCall());
+ request.on('end', mustCall(() => {
+ client.destroy();
+ server.close();
+ }));
+ request.end();
+ request.resume();
+ }));
+ }));
+}
+
+{
+ // Should be able to respond to HEAD with just .end
+ const server = createServer(mustCall((request, response) => {
+ response.end('data', mustCall());
+ response.end(mustNotCall());
+ }));
+ server.listen(0, mustCall(() => {
+ const { port } = server.address();
+ const url = `http://localhost:${port}`;
+ const client = connect(url, mustCall(() => {
+ const headers = {
+ ':path': '/',
+ ':method': 'HEAD',
+ ':scheme': 'http',
+ ':authority': `localhost:${port}`
+ };
+ const request = client.request(headers);
+ request.on('response', mustCall((headers, flags) => {
+ strictEqual(headers[HTTP2_HEADER_STATUS], HTTP_STATUS_OK);
+ strictEqual(flags, 5); // the end of stream flag is set
+ }));
+ request.on('data', mustNotCall());
+ request.on('end', mustCall(() => {
+ client.destroy();
+ server.close();
+ }));
+ request.end();
+ request.resume();
+ }));
+ }));
+}
diff --git a/test/parallel/test-http2-compat-serverresponse-finished.js b/test/parallel/test-http2-compat-serverresponse-finished.js
index cd17ff492b..b816b92220 100644
--- a/test/parallel/test-http2-compat-serverresponse-finished.js
+++ b/test/parallel/test-http2-compat-serverresponse-finished.js
@@ -5,19 +5,23 @@ if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
const h2 = require('http2');
+const net = require('net');
// Http2ServerResponse.finished
const server = h2.createServer();
server.listen(0, common.mustCall(function() {
const port = server.address().port;
server.once('request', common.mustCall(function(request, response) {
+ assert.ok(response.socket instanceof net.Socket);
+ assert.ok(response.connection instanceof net.Socket);
+ assert.strictEqual(response.socket, response.connection);
+
response.on('finish', common.mustCall(function() {
- assert.ok(request.stream !== undefined);
- assert.ok(response.stream !== undefined);
- server.close();
+ assert.strictEqual(response.socket, undefined);
+ assert.strictEqual(response.connection, undefined);
process.nextTick(common.mustCall(() => {
- assert.strictEqual(request.stream, undefined);
- assert.strictEqual(response.stream, undefined);
+ assert.ok(response.stream);
+ server.close();
}));
}));
assert.strictEqual(response.finished, false);
diff --git a/test/parallel/test-http2-compat-serverresponse-flushheaders.js b/test/parallel/test-http2-compat-serverresponse-flushheaders.js
index 48bf4dd2d4..68d4789f69 100644
--- a/test/parallel/test-http2-compat-serverresponse-flushheaders.js
+++ b/test/parallel/test-http2-compat-serverresponse-flushheaders.js
@@ -15,18 +15,23 @@ server.listen(0, common.mustCall(function() {
const port = server.address().port;
server.once('request', common.mustCall(function(request, response) {
assert.strictEqual(response.headersSent, false);
+ assert.strictEqual(response._header, false); // alias for headersSent
response.flushHeaders();
assert.strictEqual(response.headersSent, true);
+ assert.strictEqual(response._header, true);
response.flushHeaders(); // Idempotent
common.expectsError(() => {
response.writeHead(400, { 'foo-bar': 'abc123' });
}, {
- code: 'ERR_HTTP2_INFO_HEADERS_AFTER_RESPOND'
+ code: 'ERR_HTTP2_HEADERS_SENT'
});
response.on('finish', common.mustCall(function() {
server.close();
+ process.nextTick(() => {
+ response.flushHeaders(); // Idempotent
+ });
}));
serverResponse = response;
}));
diff --git a/test/parallel/test-http2-compat-serverresponse-headers-after-destroy.js b/test/parallel/test-http2-compat-serverresponse-headers-after-destroy.js
new file mode 100644
index 0000000000..fb1e369f78
--- /dev/null
+++ b/test/parallel/test-http2-compat-serverresponse-headers-after-destroy.js
@@ -0,0 +1,47 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+const assert = require('assert');
+const h2 = require('http2');
+
+// makes sure that Http2ServerResponse setHeader & removeHeader, do not throw
+// any errors if the stream was destroyed before headers were sent
+
+const server = h2.createServer();
+server.listen(0, common.mustCall(function() {
+ const port = server.address().port;
+ server.once('request', common.mustCall(function(request, response) {
+ response.destroy();
+
+ response.on('finish', common.mustCall(() => {
+ assert.strictEqual(response.headersSent, false);
+ assert.doesNotThrow(() => response.setHeader('test', 'value'));
+ assert.doesNotThrow(() => response.removeHeader('test', 'value'));
+
+ process.nextTick(() => {
+ assert.doesNotThrow(() => response.setHeader('test', 'value'));
+ assert.doesNotThrow(() => response.removeHeader('test', 'value'));
+
+ server.close();
+ });
+ }));
+ }));
+
+ const url = `http://localhost:${port}`;
+ const client = h2.connect(url, common.mustCall(function() {
+ const headers = {
+ ':path': '/',
+ ':method': 'GET',
+ ':scheme': 'http',
+ ':authority': `localhost:${port}`
+ };
+ const request = client.request(headers);
+ request.on('end', common.mustCall(function() {
+ client.destroy();
+ }));
+ request.end();
+ request.resume();
+ }));
+}));
diff --git a/test/parallel/test-http2-compat-serverresponse-headers.js b/test/parallel/test-http2-compat-serverresponse-headers.js
index 25ec4fc11b..aae1d96c55 100644
--- a/test/parallel/test-http2-compat-serverresponse-headers.js
+++ b/test/parallel/test-http2-compat-serverresponse-headers.js
@@ -124,14 +124,44 @@ server.listen(0, common.mustCall(function() {
response.sendDate = false;
assert.strictEqual(response.sendDate, false);
- assert.strictEqual(response.code, h2.constants.NGHTTP2_NO_ERROR);
-
response.on('finish', common.mustCall(function() {
- assert.strictEqual(response.code, h2.constants.NGHTTP2_NO_ERROR);
assert.strictEqual(response.headersSent, true);
+
+ common.expectsError(
+ () => response.setHeader(real, expectedValue),
+ {
+ code: 'ERR_HTTP2_HEADERS_SENT',
+ type: Error,
+ message: 'Response has already been initiated.'
+ }
+ );
+ common.expectsError(
+ () => response.removeHeader(real, expectedValue),
+ {
+ code: 'ERR_HTTP2_HEADERS_SENT',
+ type: Error,
+ message: 'Response has already been initiated.'
+ }
+ );
+
process.nextTick(() => {
- // can access headersSent after stream is undefined
- assert.strictEqual(response.stream, undefined);
+ common.expectsError(
+ () => response.setHeader(real, expectedValue),
+ {
+ code: 'ERR_HTTP2_HEADERS_SENT',
+ type: Error,
+ message: 'Response has already been initiated.'
+ }
+ );
+ common.expectsError(
+ () => response.removeHeader(real, expectedValue),
+ {
+ code: 'ERR_HTTP2_HEADERS_SENT',
+ type: Error,
+ message: 'Response has already been initiated.'
+ }
+ );
+
assert.strictEqual(response.headersSent, true);
server.close();
});
diff --git a/test/parallel/test-http2-compat-serverresponse-writehead.js b/test/parallel/test-http2-compat-serverresponse-writehead.js
index f6d74630fd..704f199ca2 100644
--- a/test/parallel/test-http2-compat-serverresponse-writehead.js
+++ b/test/parallel/test-http2-compat-serverresponse-writehead.js
@@ -16,7 +16,7 @@ server.listen(0, common.mustCall(function() {
response.writeHead(418, { 'foo-bar': 'abc123' }); // Override
common.expectsError(() => { response.writeHead(300); }, {
- code: 'ERR_HTTP2_INFO_HEADERS_AFTER_RESPOND'
+ code: 'ERR_HTTP2_HEADERS_SENT'
});
response.on('finish', common.mustCall(function() {
diff --git a/test/parallel/test-http2-compat-socket-set.js b/test/parallel/test-http2-compat-socket-set.js
new file mode 100644
index 0000000000..7411fef602
--- /dev/null
+++ b/test/parallel/test-http2-compat-socket-set.js
@@ -0,0 +1,107 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+const assert = require('assert');
+const h2 = require('http2');
+
+// Tests behaviour of the proxied socket in Http2ServerRequest
+// & Http2ServerResponse - specifically property setters
+
+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.'
+};
+
+const server = h2.createServer();
+
+server.on('request', common.mustCall(function(request, response) {
+ const noop = () => {};
+
+ assert.strictEqual(request.stream.destroyed, false);
+ request.socket.destroyed = true;
+ assert.strictEqual(request.stream.destroyed, true);
+ request.socket.destroyed = false;
+
+ assert.strictEqual(request.stream.readable, false);
+ request.socket.readable = true;
+ assert.strictEqual(request.stream.readable, true);
+
+ assert.strictEqual(request.stream.writable, true);
+ request.socket.writable = false;
+ assert.strictEqual(request.stream.writable, false);
+
+ const realOn = request.stream.on;
+ request.socket.on = noop;
+ assert.strictEqual(request.stream.on, noop);
+ request.stream.on = realOn;
+
+ const realOnce = request.stream.once;
+ request.socket.once = noop;
+ assert.strictEqual(request.stream.once, noop);
+ request.stream.once = realOnce;
+
+ const realEnd = request.stream.end;
+ request.socket.end = noop;
+ assert.strictEqual(request.stream.end, noop);
+ request.socket.end = common.mustCall();
+ request.socket.end();
+ request.stream.end = realEnd;
+
+ const realEmit = request.stream.emit;
+ request.socket.emit = noop;
+ assert.strictEqual(request.stream.emit, noop);
+ request.stream.emit = realEmit;
+
+ const realDestroy = request.stream.destroy;
+ request.socket.destroy = noop;
+ assert.strictEqual(request.stream.destroy, noop);
+ request.stream.destroy = realDestroy;
+
+ request.socket.setTimeout = noop;
+ assert.strictEqual(request.stream.session.setTimeout, noop);
+
+ assert.strictEqual(request.stream.session.socket._isProcessing, undefined);
+ request.socket._isProcessing = true;
+ assert.strictEqual(request.stream.session.socket._isProcessing, true);
+
+ common.expectsError(() => request.socket.read = noop, errMsg);
+ common.expectsError(() => request.socket.write = noop, errMsg);
+ common.expectsError(() => request.socket.pause = noop, errMsg);
+ common.expectsError(() => request.socket.resume = noop, errMsg);
+
+ request.stream.on('finish', common.mustCall(() => {
+ setImmediate(() => {
+ request.socket.setTimeout = noop;
+ assert.strictEqual(request.stream.setTimeout, noop);
+
+ assert.strictEqual(request.stream._isProcessing, undefined);
+ request.socket._isProcessing = true;
+ assert.strictEqual(request.stream._isProcessing, true);
+ });
+ }));
+ response.stream.destroy();
+}));
+
+server.listen(0, common.mustCall(function() {
+ const port = server.address().port;
+ const url = `http://localhost:${port}`;
+ const client = h2.connect(url, common.mustCall(function() {
+ 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();
+ }));
+}));
diff --git a/test/parallel/test-http2-compat-socket.js b/test/parallel/test-http2-compat-socket.js
new file mode 100644
index 0000000000..8292232f74
--- /dev/null
+++ b/test/parallel/test-http2-compat-socket.js
@@ -0,0 +1,89 @@
+'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 in Http2ServerRequest
+// & Http2ServerResponse - this proxy socket should mimic the
+// behaviour of http1 but against the http2 api & model
+
+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.'
+};
+
+const server = h2.createServer();
+
+server.on('request', common.mustCall(function(request, response) {
+ assert.ok(request.socket instanceof net.Socket);
+ assert.ok(response.socket instanceof net.Socket);
+ assert.strictEqual(request.socket, response.socket);
+
+ assert.ok(request.socket.readable);
+ request.resume();
+ assert.ok(request.socket.writable);
+ assert.strictEqual(request.socket.destroyed, false);
+
+ request.socket.setTimeout(987);
+ assert.strictEqual(request.stream.session._idleTimeout, 987);
+ request.socket.setTimeout(0);
+
+ common.expectsError(() => request.socket.read(), errMsg);
+ common.expectsError(() => request.socket.write(), errMsg);
+ common.expectsError(() => request.socket.pause(), errMsg);
+ common.expectsError(() => request.socket.resume(), errMsg);
+
+ // should have correct this context for socket methods & getters
+ assert.ok(request.socket.address() != null);
+ assert.ok(request.socket.remotePort);
+
+ request.on('end', common.mustCall(() => {
+ assert.strictEqual(request.socket.readable, false);
+ assert.doesNotThrow(() => response.socket.destroy());
+ }));
+ response.on('finish', common.mustCall(() => {
+ assert.ok(request.socket);
+ assert.strictEqual(response.socket, undefined);
+ assert.ok(request.socket.destroyed);
+ assert.strictEqual(request.socket.readable, false);
+ process.nextTick(() => {
+ assert.strictEqual(request.socket.writable, false);
+ server.close();
+ });
+ }));
+
+ // properties that do not exist on the proxy are retrieved from the socket
+ assert.ok(request.socket._server);
+ assert.strictEqual(request.socket.connecting, false);
+
+ // socket events are bound and emitted on Http2Stream
+ request.socket.on('streamClosed', common.mustCall());
+ request.socket.once('streamClosed', common.mustCall());
+ request.socket.on('testEvent', common.mustCall());
+ request.socket.emit('testEvent');
+}));
+
+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();
+ }));
+ request.end();
+ request.resume();
+ }));
+}));
diff --git a/test/parallel/test-http2-options-max-reserved-streams.js b/test/parallel/test-http2-options-max-reserved-streams.js
index 5e61907770..17009a4c11 100644
--- a/test/parallel/test-http2-options-max-reserved-streams.js
+++ b/test/parallel/test-http2-options-max-reserved-streams.js
@@ -3,6 +3,7 @@
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
+const assert = require('assert');
const h2 = require('http2');
const server = h2.createServer();
@@ -32,11 +33,9 @@ server.on('stream', common.mustCall((stream) => {
}, common.mustCall((pushedStream) => {
pushedStream.respond({ ':status': 200 });
pushedStream.on('aborted', common.mustCall());
- pushedStream.on('error', common.mustCall(common.expectsError({
- code: 'ERR_HTTP2_STREAM_ERROR',
- type: Error,
- message: 'Stream closed with error code 8'
- })));
+ pushedStream.on('error', common.mustNotCall());
+ pushedStream.on('streamClosed',
+ common.mustCall((code) => assert.strictEqual(code, 8)));
}));
stream.end('hello world');
diff --git a/test/parallel/test-http2-server-rst-stream.js b/test/parallel/test-http2-server-rst-stream.js
index edaaed35c6..b92217dc99 100644
--- a/test/parallel/test-http2-server-rst-stream.js
+++ b/test/parallel/test-http2-server-rst-stream.js
@@ -17,7 +17,7 @@ const {
NGHTTP2_INTERNAL_ERROR
} = http2.constants;
-const errCheck = common.expectsError({ code: 'ERR_HTTP2_STREAM_ERROR' }, 8);
+const errCheck = common.expectsError({ code: 'ERR_HTTP2_STREAM_ERROR' }, 6);
function checkRstCode(rstMethod, expectRstCode) {
const server = http2.createServer();
@@ -32,8 +32,11 @@ function checkRstCode(rstMethod, expectRstCode) {
else
stream[rstMethod]();
- if (expectRstCode > NGHTTP2_NO_ERROR) {
+ if (expectRstCode !== NGHTTP2_NO_ERROR &&
+ expectRstCode !== NGHTTP2_CANCEL) {
stream.on('error', common.mustCall(errCheck));
+ } else {
+ stream.on('error', common.mustNotCall());
}
});
@@ -58,8 +61,11 @@ function checkRstCode(rstMethod, expectRstCode) {
req.on('aborted', common.mustCall());
req.on('end', common.mustCall());
- if (expectRstCode > NGHTTP2_NO_ERROR) {
+ if (expectRstCode !== NGHTTP2_NO_ERROR &&
+ expectRstCode !== NGHTTP2_CANCEL) {
req.on('error', common.mustCall(errCheck));
+ } else {
+ req.on('error', common.mustNotCall());
}
}));
diff --git a/test/parallel/test-http2-stream-destroy-event-order.js b/test/parallel/test-http2-stream-destroy-event-order.js
new file mode 100644
index 0000000000..16bb5e6a62
--- /dev/null
+++ b/test/parallel/test-http2-stream-destroy-event-order.js
@@ -0,0 +1,28 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+const assert = require('assert');
+const http2 = require('http2');
+
+let client;
+let req;
+const server = http2.createServer();
+server.on('stream', common.mustCall((stream) => {
+ stream.on('error', common.mustCall(() => {
+ stream.on('streamClosed', common.mustCall((code) => {
+ assert.strictEqual(code, 2);
+ client.destroy();
+ server.close();
+ }));
+ }));
+
+ req.rstStream(2);
+}));
+server.listen(0, common.mustCall(() => {
+ client = http2.connect(`http://localhost:${server.address().port}`);
+ req = client.request();
+ req.resume();
+ req.on('error', common.mustCall());
+}));