diff --git a/deps/ngtcp2/ngtcp2.gyp b/deps/ngtcp2/ngtcp2.gyp index 3a9b066cfe..91e3a68bdd 100644 --- a/deps/ngtcp2/ngtcp2.gyp +++ b/deps/ngtcp2/ngtcp2.gyp @@ -266,8 +266,18 @@ 'HAVE_CONFIG_H', 'WITH_EXAMPLE_OSSL', 'EV_STANDALONE=1', + 'HAVE_UNISTD_H', + 'HAVE_ARPA_INET_H', + 'HAVE_NETINET_IN_H', + 'HAVE_NETINET_IP_H', ], 'conditions': [ + ['OS=="aix" or OS=="win"', { + # AIX does not support some of the networking features used in + # the test server. Windows also lacks the Unix-specific headers + # and system calls required by the ngtcp2 examples. + 'type': 'none', # Disable as executable on AIX and Windows + }], ['OS=="mac"', { 'defines': [ '__APPLE_USE_RFC_3542', @@ -281,25 +291,6 @@ 'libraries': [ '-lsocket', '-lnsl' ], }, }], - ['OS=="win"', { - 'defines': [ - 'WIN32', - '_WINDOWS', - ], - 'msvs_settings': { - 'VCCLCompilerTool': { - 'CompileAs': '1' - }, - }, - }], - ['OS!="win"', { - 'defines': [ - 'HAVE_UNISTD_H', - 'HAVE_ARPA_INET_H', - 'HAVE_NETINET_IN_H', - 'HAVE_NETINET_IP_H', - ], - }], [ 'OS=="linux" or OS=="openharmony"', { 'link_settings': { 'libraries': [ '-ldl', '-lrt' ], @@ -333,8 +324,18 @@ 'HAVE_CONFIG_H', 'WITH_EXAMPLE_OSSL', 'EV_STANDALONE=1', + 'HAVE_UNISTD_H', + 'HAVE_ARPA_INET_H', + 'HAVE_NETINET_IN_H', + 'HAVE_NETINET_IP_H', ], 'conditions': [ + ['OS=="aix" or OS=="win"', { + # AIX does not support some of the networking features used in + # the test client. Windows also lacks the Unix-specific headers + # and system calls required by the ngtcp2 examples. + 'type': 'none', # Disable as executable on AIX and Windows + }], ['OS=="mac"', { 'defines': [ '__APPLE_USE_RFC_3542', @@ -348,25 +349,6 @@ 'libraries': [ '-lsocket', '-lnsl' ], }, }], - ['OS=="win"', { - 'defines': [ - 'WIN32', - '_WINDOWS', - ], - 'msvs_settings': { - 'VCCLCompilerTool': { - 'CompileAs': '1' - }, - }, - }], - ['OS!="win"', { - 'defines': [ - 'HAVE_UNISTD_H', - 'HAVE_ARPA_INET_H', - 'HAVE_NETINET_IN_H', - 'HAVE_NETINET_IP_H', - ], - }], [ 'OS=="linux" or OS=="openharmony"', { 'link_settings': { 'libraries': [ '-ldl', '-lrt' ], diff --git a/test/common/quic/test-client.mjs b/test/common/quic/test-client.mjs new file mode 100644 index 0000000000..1e450e99ed --- /dev/null +++ b/test/common/quic/test-client.mjs @@ -0,0 +1,64 @@ +import { resolve } from 'node:path'; +import { spawn } from 'node:child_process'; + +export default class QuicTestClient { + #pathToClient; + #runningProcess; + + constructor() { + this.#pathToClient = resolve(process.execPath, '../ngtcp2_test_client'); + console.log(this.#pathToClient); + } + + help(options = { stdio: 'inherit' }) { + const { promise, resolve, reject } = Promise.withResolvers(); + const proc = spawn(this.#pathToClient, ['--help'], options); + proc.on('error', reject); + proc.on('exit', (code, signal) => { + if (code === 0) { + resolve(); + } else { + reject(new Error(`Process exited with code ${code} and signal ${signal}`)); + } + }); + return promise; + } + + run(address, port, uri, options = { stdio: 'inherit' }) { + const { promise, resolve, reject } = Promise.withResolvers(); + if (this.#runningProcess) { + reject(new Error('Server is already running')); + return promise; + } + const args = [ + address, + port, + uri ?? '', + ]; + this.#runningProcess = spawn(this.#pathToClient, args, options); + this.#runningProcess.on('error', (err) => { + this.#runningProcess = undefined; + reject(err); + }); + this.#runningProcess.on('exit', (code, signal) => { + if (code === 0) { + resolve(); + } else { + if (code === null && signal === 'SIGTERM') { + // Normal termination due to stop() being called. + resolve(); + return; + } + reject(new Error(`Process exited with code ${code} and signal ${signal}`)); + } + }); + return promise; + } + + stop() { + if (this.#runningProcess) { + this.#runningProcess.kill(); + this.#runningProcess = undefined; + } + } +}; diff --git a/test/common/quic/test-server.mjs b/test/common/quic/test-server.mjs new file mode 100644 index 0000000000..5d0168ed23 --- /dev/null +++ b/test/common/quic/test-server.mjs @@ -0,0 +1,66 @@ +import { resolve } from 'node:path'; +import { spawn } from 'node:child_process'; + +export default class QuicTestServer { + #pathToServer; + #runningProcess; + + constructor() { + this.#pathToServer = resolve(process.execPath, '../ngtcp2_test_server'); + console.log(this.#pathToServer); + } + + help(options = { stdio: 'inherit' }) { + const { promise, resolve, reject } = Promise.withResolvers(); + const proc = spawn(this.#pathToServer, ['--help'], options); + proc.on('error', reject); + proc.on('exit', (code, signal) => { + if (code === 0) { + resolve(); + } else { + reject(new Error(`Process exited with code ${code} and signal ${signal}`)); + } + }); + return promise; + } + + run(address, port, keyFile, certFile, options = { stdio: 'inherit' }) { + const { promise, resolve, reject } = Promise.withResolvers(); + if (this.#runningProcess) { + reject(new Error('Server is already running')); + return promise; + } + const args = [ + address, + port, + keyFile, + certFile, + ]; + this.#runningProcess = spawn(this.#pathToServer, args, options); + this.#runningProcess.on('error', (err) => { + this.#runningProcess = undefined; + reject(err); + }); + this.#runningProcess.on('exit', (code, signal) => { + this.#runningProcess = undefined; + if (code === 0) { + resolve(); + } else { + if (code === null && signal === 'SIGTERM') { + // Normal termination due to stop() being called. + resolve(); + return; + } + reject(new Error(`Process exited with code ${code} and signal ${signal}`)); + } + }); + return promise; + } + + stop() { + if (this.#runningProcess) { + this.#runningProcess.kill(); + this.#runningProcess = undefined; + } + } +}; diff --git a/test/parallel/test-quic-test-client.mjs b/test/parallel/test-quic-test-client.mjs new file mode 100644 index 0000000000..921001e84d --- /dev/null +++ b/test/parallel/test-quic-test-client.mjs @@ -0,0 +1,32 @@ +// Flags: --experimental-quic +import { hasQuic, isAIX, isWindows, skip } from '../common/index.mjs'; +import { rejects } from 'node:assert'; + +if (!hasQuic) { + skip('QUIC support is not enabled'); +} +if (isAIX) { + // AIX does not support some of the networking features used in the ngtcp2 + // example server and client. + skip('QUIC third-party tests are disabled on AIX'); +} +if (isWindows) { + // Windows does not support the [Li/U]nix specific headers and system calls + // required by the ngtcp2 example server/client. + skip('QUIC third-party tests are disabled on Windows'); +} + +const { default: QuicTestClient } = await import('../common/quic/test-client.mjs'); + +const client = new QuicTestClient(); + +// If this completes without throwing, the test passes. +await client.help({ stdio: 'ignore' }); + +setTimeout(() => { + client.stop(); +}, 100); + +// We expect this to fail since there's no server running. +await rejects(client.run('localhost', '12345', undefined, { stdio: 'ignore' }), + { message: /Process exited with code 1 and signal null/ }); diff --git a/test/parallel/test-quic-test-server.mjs b/test/parallel/test-quic-test-server.mjs new file mode 100644 index 0000000000..0951984b2a --- /dev/null +++ b/test/parallel/test-quic-test-server.mjs @@ -0,0 +1,34 @@ +// Flags: --experimental-quic +import { hasQuic, isAIX, isWindows, skip } from '../common/index.mjs'; + +if (!hasQuic) { + skip('QUIC support is not enabled'); +} +if (isAIX) { + // AIX does not support some of the networking features used in the ngtcp2 + // example server and client. + skip('QUIC third-party tests are disabled on AIX'); +} +if (isWindows) { + // Windows does not support the [Li/U]nix specific headers and system calls + // required by the ngtcp2 example server/client. + skip('QUIC third-party tests are disabled on Windows'); +} + +const { default: QuicTestServer } = await import('../common/quic/test-server.mjs'); +const fixtures = await import('../common/fixtures.mjs'); + +const server = new QuicTestServer(); +const fixturesPath = fixtures.path(); + +// If this completes without throwing, the test passes. +await server.help({ stdio: 'ignore' }); + +setTimeout(() => { + server.stop(); +}, 100); + +await server.run('localhost', '12345', + `${fixturesPath}/keys/agent1-key.pem`, + `${fixturesPath}/keys/agent1-cert.pem`, + { stdio: 'inherit' });