Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 5833007

Browse filesBrowse files
mmomtchevtargos
authored andcommitted
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: #35475 PR-URL: #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>
1 parent 883ed4b commit 5833007
Copy full SHA for 5833007

File tree

Expand file treeCollapse file tree

4 files changed

+106
-2
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

4 files changed

+106
-2
lines changed
Open diff view settings
Collapse file

‎lib/internal/http2/core.js‎

Copy file name to clipboardExpand all lines: lib/internal/http2/core.js
+13-2Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,7 +1037,7 @@ function finishSessionClose(session, error) {
10371037
if (socket && !socket.destroyed) {
10381038
// Always wait for writable side to finish.
10391039
socket.end((err) => {
1040-
debugSessionObj(session, 'finishSessionClose socket end', err);
1040+
debugSessionObj(session, 'finishSessionClose socket end', err, error);
10411041
// Due to the way the underlying stream is handled in Http2Session we
10421042
// won't get graceful Readable end from the other side even if it was sent
10431043
// as the stream is already considered closed and will neither be read
@@ -1055,7 +1055,7 @@ function finishSessionClose(session, error) {
10551055
}
10561056

10571057
function closeSession(session, code, error) {
1058-
debugSessionObj(session, 'start closing/destroying');
1058+
debugSessionObj(session, 'start closing/destroying', error);
10591059

10601060
const state = session[kState];
10611061
state.flags |= SESSION_FLAGS_DESTROYED;
@@ -3140,6 +3140,17 @@ function connect(authority, options, listener) {
31403140

31413141
if (typeof listener === 'function')
31423142
session.once('connect', listener);
3143+
3144+
debug('Http2Session connect', options.createConnection);
3145+
// Socket already has some buffered data - emulate receiving it
3146+
// https://github.com/nodejs/node/issues/35475
3147+
if (typeof options.createConnection === 'function') {
3148+
let buf;
3149+
while ((buf = socket.read()) !== null) {
3150+
debug(`Http2Session connect: injecting ${buf.length} already in buffer`);
3151+
session[kHandle].receive(buf);
3152+
}
3153+
}
31433154
return session;
31443155
}
31453156

Collapse file

‎src/node_http2.cc‎

Copy file name to clipboardExpand all lines: src/node_http2.cc
+28Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1829,6 +1829,33 @@ void Http2Session::Consume(Local<Object> stream_obj) {
18291829
Debug(this, "i/o stream consumed");
18301830
}
18311831

1832+
// Allow injecting of data from JS
1833+
// This is used when the socket has already some data received
1834+
// before our listener was attached
1835+
// https://github.com/nodejs/node/issues/35475
1836+
void Http2Session::Receive(const FunctionCallbackInfo<Value>& args) {
1837+
Http2Session* session;
1838+
ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
1839+
CHECK(args[0]->IsObject());
1840+
1841+
ArrayBufferViewContents<char> buffer(args[0]);
1842+
const char* data = buffer.data();
1843+
size_t len = buffer.length();
1844+
Debug(session, "Receiving %zu bytes injected from JS", len);
1845+
1846+
// Copy given buffer
1847+
while (len > 0) {
1848+
uv_buf_t buf = session->OnStreamAlloc(len);
1849+
size_t copy = buf.len > len ? len : buf.len;
1850+
memcpy(buf.base, data, copy);
1851+
buf.len = copy;
1852+
session->OnStreamRead(copy, buf);
1853+
1854+
data += copy;
1855+
len -= copy;
1856+
}
1857+
}
1858+
18321859
Http2Stream* Http2Stream::New(Http2Session* session,
18331860
int32_t id,
18341861
nghttp2_headers_category category,
@@ -3054,6 +3081,7 @@ void Initialize(Local<Object> target,
30543081
env->SetProtoMethod(session, "altsvc", Http2Session::AltSvc);
30553082
env->SetProtoMethod(session, "ping", Http2Session::Ping);
30563083
env->SetProtoMethod(session, "consume", Http2Session::Consume);
3084+
env->SetProtoMethod(session, "receive", Http2Session::Receive);
30573085
env->SetProtoMethod(session, "destroy", Http2Session::Destroy);
30583086
env->SetProtoMethod(session, "goaway", Http2Session::Goaway);
30593087
env->SetProtoMethod(session, "settings", Http2Session::Settings);
Collapse file

‎src/node_http2.h‎

Copy file name to clipboardExpand all lines: src/node_http2.h
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,7 @@ class Http2Session : public AsyncWrap,
694694
// The JavaScript API
695695
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
696696
static void Consume(const v8::FunctionCallbackInfo<v8::Value>& args);
697+
static void Receive(const v8::FunctionCallbackInfo<v8::Value>& args);
697698
static void Destroy(const v8::FunctionCallbackInfo<v8::Value>& args);
698699
static void Settings(const v8::FunctionCallbackInfo<v8::Value>& args);
699700
static void Request(const v8::FunctionCallbackInfo<v8::Value>& args);
Collapse file
+64Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Flags: --expose-internals
2+
'use strict';
3+
const common = require('../common');
4+
if (!common.hasCrypto)
5+
common.skip('missing crypto');
6+
7+
if (!common.hasMultiLocalhost())
8+
common.skip('platform-specific test.');
9+
10+
const http2 = require('http2');
11+
const assert = require('assert');
12+
const tls = require('tls');
13+
const fixtures = require('../common/fixtures');
14+
15+
const serverOptions = {
16+
key: fixtures.readKey('agent1-key.pem'),
17+
cert: fixtures.readKey('agent1-cert.pem')
18+
};
19+
const server = http2.createSecureServer(serverOptions, (req, res) => {
20+
console.log(`Connect from: ${req.connection.remoteAddress}`);
21+
assert.strictEqual(req.connection.remoteAddress, '127.0.0.2');
22+
23+
req.on('end', common.mustCall(() => {
24+
res.writeHead(200, { 'Content-Type': 'text/plain' });
25+
res.end(`You are from: ${req.connection.remoteAddress}`);
26+
}));
27+
req.resume();
28+
});
29+
30+
server.listen(0, '127.0.0.1', common.mustCall(() => {
31+
const options = {
32+
ALPNProtocols: ['h2'],
33+
host: '127.0.0.1',
34+
servername: 'localhost',
35+
localAddress: '127.0.0.2',
36+
port: server.address().port,
37+
rejectUnauthorized: false
38+
};
39+
40+
console.log('Server ready', server.address().port);
41+
42+
const socket = tls.connect(options, async () => {
43+
44+
console.log('TLS Connected!');
45+
46+
setTimeout(() => {
47+
48+
const client = http2.connect(
49+
'https://localhost:' + server.address().port,
50+
{ ...options, createConnection: () => socket }
51+
);
52+
const req = client.request({
53+
':path': '/'
54+
});
55+
req.on('data', () => req.resume());
56+
req.on('end', common.mustCall(function() {
57+
client.close();
58+
req.close();
59+
server.close();
60+
}));
61+
req.end();
62+
}, 1000);
63+
});
64+
}));

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.