Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMomtchil Momtchev <momtchil@momtchev.com>2020-10-16 14:41:09 +0300
committerNode.js GitHub Bot <github-bot@iojs.org>2020-10-25 13:29:45 +0300
commit75202d971d3a18824f357bbb31671291a1dc4e84 (patch)
tree011cb4585690540a284cd23a9d70686ea512fc3b
parent629e1ab5aa84b75cd26ee9208c909eff500a3a88 (diff)
http2: reinject data received before http2 is attached
Reinject the data already received from the TLS socket when the HTTP2 client is attached with a delay Fixes: https://github.com/nodejs/node/issues/35475 PR-URL: https://github.com/nodejs/node/pull/35678 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Alba Mendez <me@alba.sh> Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com> Reviewed-By: Ricky Zhou <0x19951125@gmail.com>
-rw-r--r--lib/internal/http2/core.js15
-rw-r--r--src/node_http2.cc28
-rw-r--r--src/node_http2.h1
-rw-r--r--test/parallel/test-http2-connect-tls-with-delay.js64
4 files changed, 106 insertions, 2 deletions
diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js
index 61b8466d6f5..a938d543ed8 100644
--- a/lib/internal/http2/core.js
+++ b/lib/internal/http2/core.js
@@ -1037,7 +1037,7 @@ function finishSessionClose(session, error) {
if (socket && !socket.destroyed) {
// Always wait for writable side to finish.
socket.end((err) => {
- debugSessionObj(session, 'finishSessionClose socket end', err);
+ debugSessionObj(session, 'finishSessionClose socket end', err, error);
// Due to the way the underlying stream is handled in Http2Session we
// won't get graceful Readable end from the other side even if it was sent
// as the stream is already considered closed and will neither be read
@@ -1055,7 +1055,7 @@ function finishSessionClose(session, error) {
}
function closeSession(session, code, error) {
- debugSessionObj(session, 'start closing/destroying');
+ debugSessionObj(session, 'start closing/destroying', error);
const state = session[kState];
state.flags |= SESSION_FLAGS_DESTROYED;
@@ -3140,6 +3140,17 @@ function connect(authority, options, listener) {
if (typeof listener === 'function')
session.once('connect', listener);
+
+ debug('Http2Session connect', options.createConnection);
+ // Socket already has some buffered data - emulate receiving it
+ // https://github.com/nodejs/node/issues/35475
+ if (typeof options.createConnection === 'function') {
+ let buf;
+ while ((buf = socket.read()) !== null) {
+ debug(`Http2Session connect: injecting ${buf.length} already in buffer`);
+ session[kHandle].receive(buf);
+ }
+ }
return session;
}
diff --git a/src/node_http2.cc b/src/node_http2.cc
index b72e03e8571..b8e462419e5 100644
--- a/src/node_http2.cc
+++ b/src/node_http2.cc
@@ -1829,6 +1829,33 @@ void Http2Session::Consume(Local<Object> stream_obj) {
Debug(this, "i/o stream consumed");
}
+// Allow injecting of data from JS
+// This is used when the socket has already some data received
+// before our listener was attached
+// https://github.com/nodejs/node/issues/35475
+void Http2Session::Receive(const FunctionCallbackInfo<Value>& args) {
+ Http2Session* session;
+ ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
+ CHECK(args[0]->IsObject());
+
+ ArrayBufferViewContents<char> buffer(args[0]);
+ const char* data = buffer.data();
+ size_t len = buffer.length();
+ Debug(session, "Receiving %zu bytes injected from JS", len);
+
+ // Copy given buffer
+ while (len > 0) {
+ uv_buf_t buf = session->OnStreamAlloc(len);
+ size_t copy = buf.len > len ? len : buf.len;
+ memcpy(buf.base, data, copy);
+ buf.len = copy;
+ session->OnStreamRead(copy, buf);
+
+ data += copy;
+ len -= copy;
+ }
+}
+
Http2Stream* Http2Stream::New(Http2Session* session,
int32_t id,
nghttp2_headers_category category,
@@ -3054,6 +3081,7 @@ void Initialize(Local<Object> target,
env->SetProtoMethod(session, "altsvc", Http2Session::AltSvc);
env->SetProtoMethod(session, "ping", Http2Session::Ping);
env->SetProtoMethod(session, "consume", Http2Session::Consume);
+ env->SetProtoMethod(session, "receive", Http2Session::Receive);
env->SetProtoMethod(session, "destroy", Http2Session::Destroy);
env->SetProtoMethod(session, "goaway", Http2Session::Goaway);
env->SetProtoMethod(session, "settings", Http2Session::Settings);
diff --git a/src/node_http2.h b/src/node_http2.h
index 79cbe7613b5..306f5460691 100644
--- a/src/node_http2.h
+++ b/src/node_http2.h
@@ -694,6 +694,7 @@ class Http2Session : public AsyncWrap,
// The JavaScript API
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Consume(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void Receive(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Destroy(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Settings(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Request(const v8::FunctionCallbackInfo<v8::Value>& args);
diff --git a/test/parallel/test-http2-connect-tls-with-delay.js b/test/parallel/test-http2-connect-tls-with-delay.js
new file mode 100644
index 00000000000..3e2e8a46a36
--- /dev/null
+++ b/test/parallel/test-http2-connect-tls-with-delay.js
@@ -0,0 +1,64 @@
+// Flags: --expose-internals
+'use strict';
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+
+if (!common.hasMultiLocalhost())
+ common.skip('platform-specific test.');
+
+const http2 = require('http2');
+const assert = require('assert');
+const tls = require('tls');
+const fixtures = require('../common/fixtures');
+
+const serverOptions = {
+ key: fixtures.readKey('agent1-key.pem'),
+ cert: fixtures.readKey('agent1-cert.pem')
+};
+const server = http2.createSecureServer(serverOptions, (req, res) => {
+ console.log(`Connect from: ${req.connection.remoteAddress}`);
+ assert.strictEqual(req.connection.remoteAddress, '127.0.0.2');
+
+ req.on('end', common.mustCall(() => {
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end(`You are from: ${req.connection.remoteAddress}`);
+ }));
+ req.resume();
+});
+
+server.listen(0, '127.0.0.1', common.mustCall(() => {
+ const options = {
+ ALPNProtocols: ['h2'],
+ host: '127.0.0.1',
+ servername: 'localhost',
+ localAddress: '127.0.0.2',
+ port: server.address().port,
+ rejectUnauthorized: false
+ };
+
+ console.log('Server ready', server.address().port);
+
+ const socket = tls.connect(options, async () => {
+
+ console.log('TLS Connected!');
+
+ setTimeout(() => {
+
+ const client = http2.connect(
+ 'https://localhost:' + server.address().port,
+ { ...options, createConnection: () => socket }
+ );
+ const req = client.request({
+ ':path': '/'
+ });
+ req.on('data', () => req.resume());
+ req.on('end', common.mustCall(function() {
+ client.close();
+ req.close();
+ server.close();
+ }));
+ req.end();
+ }, 1000);
+ });
+}));