diff options
author | Ouyang Yadong <oyydoibh@gmail.com> | 2018-07-29 22:41:11 +0800 |
---|---|---|
committer | Matteo Collina <hello@matteocollina.com> | 2018-08-06 11:05:51 +0200 |
commit | 2bea9cefbc10ed1dd497bbae61c07d971da287dd (patch) | |
tree | 02bfcbc3fcf39fe7524f9271762617f23e107925 /test | |
parent | 214844ea4e561b0f77e97643d28b251cdc574089 (diff) | |
download | android-node-v8-2bea9cefbc10ed1dd497bbae61c07d971da287dd.tar.gz android-node-v8-2bea9cefbc10ed1dd497bbae61c07d971da287dd.tar.bz2 android-node-v8-2bea9cefbc10ed1dd497bbae61c07d971da287dd.zip |
dgram: implement socket.bind({ fd })
dgram: Implement binding an existing `fd`. Allow pass a `fd` property
to `socket.bind()` in dgram.
src: Add `UDPWrap::Open`
PR-URL: https://github.com/nodejs/node/pull/21745
Fixes: https://github.com/nodejs/node/issues/14961
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Diffstat (limited to 'test')
-rw-r--r-- | test/parallel/test-cluster-dgram-bind-fd.js | 108 | ||||
-rw-r--r-- | test/parallel/test-dgram-bind-fd-error.js | 55 | ||||
-rw-r--r-- | test/parallel/test-dgram-bind-fd.js | 118 | ||||
-rw-r--r-- | test/parallel/test-dgram-create-socket-handle-fd.js | 42 | ||||
-rw-r--r-- | test/parallel/test-dgram-create-socket-handle.js | 8 |
5 files changed, 323 insertions, 8 deletions
diff --git a/test/parallel/test-cluster-dgram-bind-fd.js b/test/parallel/test-cluster-dgram-bind-fd.js new file mode 100644 index 0000000000..429f932608 --- /dev/null +++ b/test/parallel/test-cluster-dgram-bind-fd.js @@ -0,0 +1,108 @@ +'use strict'; +const common = require('../common'); +if (common.isWindows) + common.skip('dgram clustering is currently not supported on Windows.'); + +const NUM_WORKERS = 4; +const PACKETS_PER_WORKER = 10; + +const assert = require('assert'); +const cluster = require('cluster'); +const dgram = require('dgram'); +const { UDP } = process.binding('udp_wrap'); + +if (cluster.isMaster) + master(); +else + worker(); + + +function master() { + // Create a handle and use its fd. + const rawHandle = new UDP(); + const err = rawHandle.bind(common.localhostIPv4, 0, 0); + assert(err >= 0, String(err)); + assert.notStrictEqual(rawHandle.fd, -1); + + const fd = rawHandle.fd; + + let listening = 0; + + // Fork 4 workers. + for (let i = 0; i < NUM_WORKERS; i++) + cluster.fork(); + + // Wait until all workers are listening. + cluster.on('listening', common.mustCall((worker, address) => { + if (++listening < NUM_WORKERS) + return; + + // Start sending messages. + const buf = Buffer.from('hello world'); + const socket = dgram.createSocket('udp4'); + let sent = 0; + doSend(); + + function doSend() { + socket.send(buf, 0, buf.length, address.port, address.address, afterSend); + } + + function afterSend() { + sent++; + if (sent < NUM_WORKERS * PACKETS_PER_WORKER) { + doSend(); + } else { + socket.close(); + } + } + }, NUM_WORKERS)); + + // Set up event handlers for every worker. Each worker sends a message when + // it has received the expected number of packets. After that it disconnects. + for (const key in cluster.workers) { + if (cluster.workers.hasOwnProperty(key)) + setupWorker(cluster.workers[key]); + } + + function setupWorker(worker) { + let received = 0; + + worker.send({ + fd, + }); + + worker.on('message', common.mustCall((msg) => { + received = msg.received; + worker.disconnect(); + })); + + worker.on('exit', common.mustCall(() => { + assert.strictEqual(received, PACKETS_PER_WORKER); + })); + } +} + + +function worker() { + let received = 0; + + process.on('message', common.mustCall((data) => { + const { fd } = data; + // Create udp socket and start listening. + const socket = dgram.createSocket('udp4'); + + socket.on('message', common.mustCall((data, info) => { + received++; + + // Every 10 messages, notify the master. + if (received === PACKETS_PER_WORKER) { + process.send({ received }); + socket.close(); + } + }, PACKETS_PER_WORKER)); + + socket.bind({ + fd, + }); + })); +} diff --git a/test/parallel/test-dgram-bind-fd-error.js b/test/parallel/test-dgram-bind-fd-error.js new file mode 100644 index 0000000000..efe0e43d7b --- /dev/null +++ b/test/parallel/test-dgram-bind-fd-error.js @@ -0,0 +1,55 @@ +// Flags: --expose-internals +'use strict'; +const common = require('../common'); +if (common.isWindows) + common.skip('Does not support binding fd on Windows'); + +const dgram = require('dgram'); +const assert = require('assert'); +const { kStateSymbol } = require('internal/dgram'); +const { TCP, constants } = process.binding('tcp_wrap'); +const TYPE = 'udp4'; + +// Throw when the fd is occupied according to https://github.com/libuv/libuv/pull/1851. +{ + const socket = dgram.createSocket(TYPE); + + socket.bind(common.mustCall(() => { + const anotherSocket = dgram.createSocket(TYPE); + const { handle } = socket[kStateSymbol]; + + common.expectsError(() => { + anotherSocket.bind({ + fd: handle.fd, + }); + }, { + code: 'EEXIST', + type: Error, + message: /^open EEXIST$/ + }); + + socket.close(); + })); +} + +// Throw when the type of fd is not "UDP". +{ + const handle = new TCP(constants.SOCKET); + handle.listen(); + + const fd = handle.fd; + assert.notStrictEqual(fd, -1); + + const socket = new dgram.createSocket(TYPE); + common.expectsError(() => { + socket.bind({ + fd, + }); + }, { + code: 'ERR_INVALID_FD_TYPE', + type: TypeError, + message: /^Unsupported fd type: TCP$/ + }); + + handle.close(); +} diff --git a/test/parallel/test-dgram-bind-fd.js b/test/parallel/test-dgram-bind-fd.js new file mode 100644 index 0000000000..c4a80abb92 --- /dev/null +++ b/test/parallel/test-dgram-bind-fd.js @@ -0,0 +1,118 @@ +'use strict'; +const common = require('../common'); +if (common.isWindows) + common.skip('Does not support binding fd on Windows'); + +const assert = require('assert'); +const dgram = require('dgram'); +const { UDP } = process.binding('udp_wrap'); +const { UV_UDP_REUSEADDR } = process.binding('constants').os; + +const BUFFER_SIZE = 4096; + +// Test binding a fd. +{ + function createHandle(reuseAddr, udp4, bindAddress) { + let flags = 0; + if (reuseAddr) + flags |= UV_UDP_REUSEADDR; + + const handle = new UDP(); + let err = 0; + + if (udp4) { + err = handle.bind(bindAddress, 0, flags); + } else { + err = handle.bind6(bindAddress, 0, flags); + } + assert(err >= 0, String(err)); + assert.notStrictEqual(handle.fd, -1); + return handle; + } + + function testWithOptions(reuseAddr, udp4) { + const type = udp4 ? 'udp4' : 'udp6'; + const bindAddress = udp4 ? common.localhostIPv4 : '::1'; + + let fd; + + const receiver = dgram.createSocket({ + type, + }); + + receiver.bind({ + port: 0, + address: bindAddress, + }, common.mustCall(() => { + const { port, address } = receiver.address(); + // Create a handle to reuse its fd. + const handle = createHandle(reuseAddr, udp4, bindAddress); + + fd = handle.fd; + assert.notStrictEqual(handle.fd, -1); + + const socket = dgram.createSocket({ + type, + recvBufferSize: BUFFER_SIZE, + sendBufferSize: BUFFER_SIZE, + }); + + socket.bind({ + port: 0, + address: bindAddress, + fd, + }, common.mustCall(() => { + // Test address(). + const rinfo = {}; + const err = handle.getsockname(rinfo); + assert.strictEqual(err, 0); + const socketRInfo = socket.address(); + assert.strictEqual(rinfo.address, socketRInfo.address); + assert.strictEqual(rinfo.port, socketRInfo.port); + + // Test buffer size. + const recvBufferSize = socket.getRecvBufferSize(); + const sendBufferSize = socket.getSendBufferSize(); + + // note: linux will double the buffer size + const expectedBufferSize = common.isLinux ? + BUFFER_SIZE * 2 : BUFFER_SIZE; + assert.strictEqual(recvBufferSize, expectedBufferSize); + assert.strictEqual(sendBufferSize, expectedBufferSize); + + socket.send(String(fd), port, address); + })); + + socket.on('message', common.mustCall((data) => { + assert.strictEqual(data.toString('utf8'), String(fd)); + socket.close(); + })); + + socket.on('error', (err) => { + console.error(err.message); + assert.fail(err.message); + }); + + socket.on('close', common.mustCall(() => {})); + })); + + receiver.on('message', common.mustCall((data, { address, port }) => { + assert.strictEqual(data.toString('utf8'), String(fd)); + receiver.send(String(fd), port, address); + process.nextTick(() => receiver.close()); + })); + + receiver.on('error', (err) => { + console.error(err.message); + assert.fail(err.message); + }); + + receiver.on('close', common.mustCall(() => {})); + } + + testWithOptions(true, true); + testWithOptions(false, true); + if (common.hasIPv6) { + testWithOptions(false, false); + } +} diff --git a/test/parallel/test-dgram-create-socket-handle-fd.js b/test/parallel/test-dgram-create-socket-handle-fd.js new file mode 100644 index 0000000000..ff507b6ec5 --- /dev/null +++ b/test/parallel/test-dgram-create-socket-handle-fd.js @@ -0,0 +1,42 @@ +'use strict'; +const common = require('../common'); +if (common.isWindows) + common.skip('Does not support binding fd on Windows'); + +const assert = require('assert'); +const dgram = require('dgram'); +const { UDP } = process.binding('udp_wrap'); +const { TCP, constants } = process.binding('tcp_wrap'); +const _createSocketHandle = dgram._createSocketHandle; + +// Return a negative number if the "existing fd" is invalid. +{ + const err = _createSocketHandle(common.localhostIPv4, 0, 'udp4', 42); + assert(err < 0); +} + +// Return a negative number if the type of fd is not "UDP". +{ + // Create a handle with fd. + const rawHandle = new UDP(); + const err = rawHandle.bind(common.localhostIPv4, 0, 0); + assert(err >= 0, String(err)); + assert.notStrictEqual(rawHandle.fd, -1); + + const handle = _createSocketHandle(null, 0, 'udp4', rawHandle.fd); + assert(handle instanceof UDP); + assert.strictEqual(typeof handle.fd, 'number'); + assert(handle.fd > 0); +} + +// Create a bound handle. +{ + const rawHandle = new TCP(constants.SOCKET); + const err = rawHandle.listen(); + assert(err >= 0, String(err)); + assert.notStrictEqual(rawHandle.fd, -1); + + const handle = _createSocketHandle(null, 0, 'udp4', rawHandle.fd); + assert(handle < 0); + rawHandle.close(); +} diff --git a/test/parallel/test-dgram-create-socket-handle.js b/test/parallel/test-dgram-create-socket-handle.js index e49e3e8dd6..3df34a95c2 100644 --- a/test/parallel/test-dgram-create-socket-handle.js +++ b/test/parallel/test-dgram-create-socket-handle.js @@ -5,14 +5,6 @@ const assert = require('assert'); const { _createSocketHandle } = require('internal/dgram'); const UDP = process.binding('udp_wrap').UDP; -// Throws if an "existing fd" is passed in. -common.expectsError(() => { - _createSocketHandle(common.localhostIPv4, 0, 'udp4', 42); -}, { - code: 'ERR_ASSERTION', - message: /^false == true$/ -}); - { // Create a handle that is not bound. const handle = _createSocketHandle(null, null, 'udp4'); |