Skip to content
This repository was archived by the owner on Aug 11, 2020. It is now read-only.

Commit 5453076

Browse files
committed
squash! quic: allow testing QUIC without real UDP handle
1 parent 4e70ee0 commit 5453076

File tree

4 files changed

+187
-1
lines changed

4 files changed

+187
-1
lines changed

src/js_udp_wrap.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ void JSUDPWrap::OnSendDone(const FunctionCallbackInfo<Value>& args) {
180180
CHECK(args[0]->IsObject());
181181
CHECK(args[1]->IsInt32());
182182
ReqWrap<uv_udp_send_t>* req_wrap;
183-
ASSIGN_OR_RETURN_UNWRAP(&wrap, args[0].As<Object>());
183+
ASSIGN_OR_RETURN_UNWRAP(&req_wrap, args[0].As<Object>());
184184
int status = args[1].As<Int32>()->Value();
185185

186186
wrap->listener()->OnSendDone(req_wrap, status);

test/common/README.md

+14
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ This directory contains modules used to test the Node.js implementation.
2121
* [Report module](#report-module)
2222
* [tick module](#tick-module)
2323
* [tmpdir module](#tmpdir-module)
24+
* [UDP pair helper](#udp-pair-helper)
2425
* [WPT module](#wpt-module)
2526

2627
## Benchmark Module
@@ -912,6 +913,19 @@ listener to process `'beforeExit'`. If a file needs to be left open until
912913
Node.js completes, use a child process and call `refresh()` only in the
913914
parent.
914915

916+
## UDP pair helper
917+
918+
The `common/udppair` module exports a function `makeUDPPair` and a class
919+
`FakeUDPWrap`.
920+
921+
`FakeUDPWrap` emits `'send'` events when data is to be sent on it, and provides
922+
an `emitReceived()` API for acting as if data has been received on it.
923+
924+
`makeUDPPair` returns an object `{ clientSide, serverSide }` where each side
925+
is an `FakeUDPWrap` connected to the other side.
926+
927+
There is no difference between client or server side beyond their names.
928+
915929
## WPT Module
916930

917931
### harness

test/common/udppair.js

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/* eslint-disable node-core/require-common-first, node-core/required-modules */
2+
'use strict';
3+
const { internalBinding } = require('internal/test/binding');
4+
const { JSUDPWrap } = internalBinding('js_udp_wrap');
5+
const EventEmitter = require('events');
6+
7+
class FakeUDPWrap extends EventEmitter {
8+
constructor() {
9+
super();
10+
11+
this._handle = new JSUDPWrap();
12+
13+
this._handle.onreadstart = () => this._startReading();
14+
this._handle.onreadstop = () => this._stopReading();
15+
this._handle.onwrite =
16+
(wrap, buffers, addr) => this._write(wrap, buffers, addr);
17+
this._handle.getsockname = (obj) => {
18+
Object.assign(obj, { address: '127.0.0.1', family: 'IPv4', port: 1337 });
19+
return 0;
20+
};
21+
22+
this.reading = false;
23+
this.bufferedReceived = [];
24+
this.emitBufferedImmediate = null;
25+
}
26+
27+
_emitBuffered = () => {
28+
if (!this.reading) return;
29+
if (this.bufferedReceived.length > 0) {
30+
this.emitReceived(this.bufferedReceived.shift());
31+
this.emitBufferedImmediate = setImmediate(this._emitBuffered);
32+
} else {
33+
this.emit('wantRead');
34+
}
35+
};
36+
37+
_startReading() {
38+
this.reading = true;
39+
this.emitBufferedImmediate = setImmediate(this._emitBuffered);
40+
}
41+
42+
_stopReading() {
43+
this.reading = false;
44+
clearImmediate(this.emitBufferedImmediate);
45+
}
46+
47+
_write(wrap, buffers, addr) {
48+
this.emit('send', { buffers, addr });
49+
setImmediate(() => this._handle.onSendDone(wrap, 0));
50+
}
51+
52+
afterBind() {
53+
this._handle.onAfterBind();
54+
}
55+
56+
emitReceived(info) {
57+
if (!this.reading) {
58+
this.bufferedReceived.push(info);
59+
return;
60+
}
61+
62+
const {
63+
buffers,
64+
addr: {
65+
family = 4,
66+
address = '127.0.0.1',
67+
port = 1337,
68+
},
69+
flags = 0
70+
} = info;
71+
72+
let familyInt;
73+
switch (family) {
74+
case 'IPv4': familyInt = 4; break;
75+
case 'IPv6': familyInt = 6; break;
76+
default: throw new Error('bad family');
77+
}
78+
79+
for (const buffer of buffers) {
80+
this._handle.emitReceived(buffer, familyInt, address, port, flags);
81+
}
82+
}
83+
}
84+
85+
function makeUDPPair() {
86+
const serverSide = new FakeUDPWrap();
87+
const clientSide = new FakeUDPWrap();
88+
89+
serverSide.on('send',
90+
(chk) => setImmediate(() => clientSide.emitReceived(chk)));
91+
clientSide.on('send',
92+
(chk) => setImmediate(() => serverSide.emitReceived(chk)));
93+
94+
return { serverSide, clientSide };
95+
}
96+
97+
module.exports = {
98+
FakeUDPWrap,
99+
makeUDPPair
100+
};
+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Flags: --expose-internals
2+
'use strict';
3+
const common = require('../common');
4+
const { makeUDPPair } = require('../common/udppair');
5+
const assert = require('assert');
6+
const quic = require('quic');
7+
const { kUDPHandleForTesting } = require('internal/quic/core');
8+
9+
const fixtures = require('../common/fixtures');
10+
const key = fixtures.readKey('agent1-key.pem', 'binary');
11+
const cert = fixtures.readKey('agent1-cert.pem', 'binary');
12+
const ca = fixtures.readKey('ca1-cert.pem', 'binary');
13+
14+
const { serverSide, clientSide } = makeUDPPair();
15+
16+
const server = quic.createSocket({
17+
port: 0, validateAddress: true, [kUDPHandleForTesting]: serverSide._handle
18+
});
19+
20+
serverSide.afterBind();
21+
server.listen({
22+
key,
23+
cert,
24+
ca,
25+
rejectUnauthorized: false,
26+
maxCryptoBuffer: 4096,
27+
alpn: 'meow'
28+
});
29+
30+
server.on('session', common.mustCall((session) => {
31+
session.on('secure', common.mustCall((servername, alpn, cipher) => {
32+
const stream = session.openStream({ halfOpen: false });
33+
stream.end('Hi!');
34+
stream.on('data', common.mustNotCall());
35+
stream.on('finish', common.mustCall());
36+
stream.on('close', common.mustNotCall());
37+
stream.on('end', common.mustNotCall());
38+
}));
39+
40+
session.on('close', common.mustNotCall());
41+
}));
42+
43+
server.on('ready', common.mustCall(() => {
44+
const client = quic.createSocket({
45+
port: 0,
46+
[kUDPHandleForTesting]: clientSide._handle,
47+
client: {
48+
key,
49+
cert,
50+
ca,
51+
alpn: 'meow'
52+
}
53+
});
54+
clientSide.afterBind();
55+
56+
const req = client.connect({
57+
address: 'localhost',
58+
port: server.address.port
59+
});
60+
61+
req.on('stream', common.mustCall((stream) => {
62+
stream.on('data', common.mustCall((data) => {
63+
assert.strictEqual(data.toString(), 'Hi!');
64+
}));
65+
66+
stream.on('end', common.mustCall());
67+
}));
68+
69+
req.on('close', common.mustNotCall());
70+
}));
71+
72+
server.on('close', common.mustNotCall());

0 commit comments

Comments
 (0)