summaryrefslogtreecommitdiffstats
path: root/test/client-upgrade.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--test/client-upgrade.js452
1 files changed, 452 insertions, 0 deletions
diff --git a/test/client-upgrade.js b/test/client-upgrade.js
new file mode 100644
index 0000000..4ccbcce
--- /dev/null
+++ b/test/client-upgrade.js
@@ -0,0 +1,452 @@
+'use strict'
+
+const { test } = require('tap')
+const { Client, errors } = require('..')
+const net = require('net')
+const http = require('http')
+const EE = require('events')
+const { kBusy } = require('../lib/core/symbols')
+
+test('basic upgrade', (t) => {
+ t.plan(6)
+
+ const server = net.createServer((c) => {
+ c.on('data', (d) => {
+ t.ok(/upgrade: websocket/i.test(d))
+ c.write('HTTP/1.1 101\r\n')
+ c.write('hello: world\r\n')
+ c.write('connection: upgrade\r\n')
+ c.write('upgrade: websocket\r\n')
+ c.write('\r\n')
+ c.write('Body')
+ })
+
+ c.on('end', () => {
+ c.end()
+ })
+ })
+ t.teardown(server.close.bind(server))
+
+ server.listen(0, () => {
+ const client = new Client(`http://localhost:${server.address().port}`)
+ t.teardown(client.close.bind(client))
+
+ const signal = new EE()
+ client.upgrade({
+ signal,
+ path: '/',
+ method: 'GET',
+ protocol: 'Websocket'
+ }, (err, data) => {
+ t.error(err)
+
+ t.equal(signal.listenerCount('abort'), 0)
+
+ const { headers, socket } = data
+
+ let recvData = ''
+ data.socket.on('data', (d) => {
+ recvData += d
+ })
+
+ socket.on('close', () => {
+ t.equal(recvData.toString(), 'Body')
+ })
+
+ t.same(headers, {
+ hello: 'world',
+ connection: 'upgrade',
+ upgrade: 'websocket'
+ })
+ socket.end()
+ })
+ t.equal(signal.listenerCount('abort'), 1)
+ })
+})
+
+test('basic upgrade promise', (t) => {
+ t.plan(2)
+
+ const server = net.createServer((c) => {
+ c.on('data', (d) => {
+ c.write('HTTP/1.1 101\r\n')
+ c.write('hello: world\r\n')
+ c.write('connection: upgrade\r\n')
+ c.write('upgrade: websocket\r\n')
+ c.write('\r\n')
+ c.write('Body')
+ })
+
+ c.on('end', () => {
+ c.end()
+ })
+ })
+ t.teardown(server.close.bind(server))
+
+ server.listen(0, async () => {
+ const client = new Client(`http://localhost:${server.address().port}`)
+ t.teardown(client.close.bind(client))
+
+ const { headers, socket } = await client.upgrade({
+ path: '/',
+ method: 'GET',
+ protocol: 'Websocket'
+ })
+
+ let recvData = ''
+ socket.on('data', (d) => {
+ recvData += d
+ })
+
+ socket.on('close', () => {
+ t.equal(recvData.toString(), 'Body')
+ })
+
+ t.same(headers, {
+ hello: 'world',
+ connection: 'upgrade',
+ upgrade: 'websocket'
+ })
+ socket.end()
+ })
+})
+
+test('upgrade error', (t) => {
+ t.plan(1)
+
+ const server = net.createServer((c) => {
+ c.on('data', (d) => {
+ c.write('HTTP/1.1 101\r\n')
+ c.write('hello: world\r\n')
+ c.write('connection: upgrade\r\n')
+ c.write('\r\n')
+ c.write('Body')
+ })
+ c.on('error', () => {
+ // Whether we get an error, end or close is undefined.
+ // Ignore error.
+ })
+ })
+ t.teardown(server.close.bind(server))
+
+ server.listen(0, async () => {
+ const client = new Client(`http://localhost:${server.address().port}`)
+ t.teardown(client.close.bind(client))
+
+ try {
+ await client.upgrade({
+ path: '/',
+ method: 'GET',
+ protocol: 'Websocket'
+ })
+ } catch (err) {
+ t.ok(err)
+ }
+ })
+})
+
+test('upgrade invalid opts', (t) => {
+ t.plan(6)
+
+ const client = new Client('http://localhost:5432')
+
+ client.upgrade(null, err => {
+ t.type(err, errors.InvalidArgumentError)
+ t.equal(err.message, 'invalid opts')
+ })
+
+ try {
+ client.upgrade(null, null)
+ t.fail()
+ } catch (err) {
+ t.type(err, errors.InvalidArgumentError)
+ t.equal(err.message, 'invalid opts')
+ }
+
+ try {
+ client.upgrade({ path: '/' }, null)
+ t.fail()
+ } catch (err) {
+ t.type(err, errors.InvalidArgumentError)
+ t.equal(err.message, 'invalid callback')
+ }
+})
+
+test('basic upgrade2', (t) => {
+ t.plan(3)
+
+ const server = http.createServer()
+ server.on('upgrade', (req, c, head) => {
+ c.write('HTTP/1.1 101\r\n')
+ c.write('hello: world\r\n')
+ c.write('connection: upgrade\r\n')
+ c.write('upgrade: websocket\r\n')
+ c.write('\r\n')
+ c.write('Body')
+ c.end()
+ })
+ t.teardown(server.close.bind(server))
+
+ server.listen(0, () => {
+ const client = new Client(`http://localhost:${server.address().port}`)
+ t.teardown(client.close.bind(client))
+
+ client.upgrade({
+ path: '/',
+ method: 'GET',
+ protocol: 'Websocket'
+ }, (err, data) => {
+ t.error(err)
+
+ const { headers, socket } = data
+
+ let recvData = ''
+ data.socket.on('data', (d) => {
+ recvData += d
+ })
+
+ socket.on('close', () => {
+ t.equal(recvData.toString(), 'Body')
+ })
+
+ t.same(headers, {
+ hello: 'world',
+ connection: 'upgrade',
+ upgrade: 'websocket'
+ })
+ socket.end()
+ })
+ })
+})
+
+test('upgrade wait for empty pipeline', (t) => {
+ t.plan(7)
+
+ let canConnect = false
+ const server = http.createServer((req, res) => {
+ res.end()
+ canConnect = true
+ })
+ server.on('upgrade', (req, c, firstBodyChunk) => {
+ t.equal(canConnect, true)
+ c.write('HTTP/1.1 101\r\n')
+ c.write('hello: world\r\n')
+ c.write('connection: upgrade\r\n')
+ c.write('upgrade: websocket\r\n')
+ c.write('\r\n')
+ c.write('Body')
+ c.end()
+ })
+ t.teardown(server.close.bind(server))
+
+ server.listen(0, async () => {
+ const client = new Client(`http://localhost:${server.address().port}`, {
+ pipelining: 3
+ })
+ t.teardown(client.close.bind(client))
+
+ client.request({
+ path: '/',
+ method: 'GET'
+ }, (err) => {
+ t.error(err)
+ })
+ client.once('connect', () => {
+ process.nextTick(() => {
+ t.equal(client[kBusy], false)
+
+ client.upgrade({
+ path: '/'
+ }, (err, { socket }) => {
+ t.error(err)
+ let recvData = ''
+ socket.on('data', (d) => {
+ recvData += d
+ })
+
+ socket.on('end', () => {
+ t.equal(recvData.toString(), 'Body')
+ })
+
+ socket.write('Body')
+ socket.end()
+ })
+ t.equal(client[kBusy], true)
+
+ client.request({
+ path: '/',
+ method: 'GET'
+ }, (err) => {
+ t.error(err)
+ })
+ })
+ })
+ })
+})
+
+test('upgrade aborted', (t) => {
+ t.plan(6)
+
+ const server = http.createServer((req, res) => {
+ t.fail()
+ })
+ server.on('upgrade', (req, c, firstBodyChunk) => {
+ t.fail()
+ })
+ t.teardown(server.close.bind(server))
+
+ server.listen(0, async () => {
+ const client = new Client(`http://localhost:${server.address().port}`, {
+ pipelining: 3
+ })
+ t.teardown(client.destroy.bind(client))
+
+ const signal = new EE()
+ client.upgrade({
+ path: '/',
+ signal,
+ opaque: 'asd'
+ }, (err, { opaque }) => {
+ t.equal(opaque, 'asd')
+ t.type(err, errors.RequestAbortedError)
+ t.equal(signal.listenerCount('abort'), 0)
+ })
+ t.equal(client[kBusy], true)
+ t.equal(signal.listenerCount('abort'), 1)
+ signal.emit('abort')
+
+ client.close(() => {
+ t.pass()
+ })
+ })
+})
+
+test('basic aborted after res', (t) => {
+ t.plan(1)
+
+ const signal = new EE()
+ const server = http.createServer()
+ server.on('upgrade', (req, c, head) => {
+ c.write('HTTP/1.1 101\r\n')
+ c.write('hello: world\r\n')
+ c.write('connection: upgrade\r\n')
+ c.write('upgrade: websocket\r\n')
+ c.write('\r\n')
+ c.write('Body')
+ c.end()
+ c.on('error', () => {
+
+ })
+ signal.emit('abort')
+ })
+ t.teardown(server.close.bind(server))
+
+ server.listen(0, () => {
+ const client = new Client(`http://localhost:${server.address().port}`)
+ t.teardown(client.close.bind(client))
+
+ client.upgrade({
+ path: '/',
+ method: 'GET',
+ protocol: 'Websocket',
+ signal
+ }, (err) => {
+ t.type(err, errors.RequestAbortedError)
+ })
+ })
+})
+
+test('basic upgrade error', (t) => {
+ t.plan(2)
+
+ const server = net.createServer((c) => {
+ c.on('data', (d) => {
+ c.write('HTTP/1.1 101\r\n')
+ c.write('hello: world\r\n')
+ c.write('connection: upgrade\r\n')
+ c.write('upgrade: websocket\r\n')
+ c.write('\r\n')
+ c.write('Body')
+ })
+ c.on('error', () => {
+
+ })
+ })
+ t.teardown(server.close.bind(server))
+
+ server.listen(0, () => {
+ const client = new Client(`http://localhost:${server.address().port}`)
+ t.teardown(client.close.bind(client))
+
+ const _err = new Error()
+ client.upgrade({
+ path: '/',
+ method: 'GET',
+ protocol: 'Websocket'
+ }, (err, data) => {
+ t.error(err)
+ data.socket.on('error', (err) => {
+ t.equal(err, _err)
+ })
+ throw _err
+ })
+ })
+})
+
+test('upgrade disconnect', (t) => {
+ t.plan(3)
+
+ const server = net.createServer(connection => {
+ connection.destroy()
+ })
+
+ t.teardown(server.close.bind(server))
+
+ server.listen(0, () => {
+ const client = new Client(`http://localhost:${server.address().port}`)
+ t.teardown(client.close.bind(client))
+
+ client.on('disconnect', (origin, [self], error) => {
+ t.equal(client, self)
+ t.type(error, Error)
+ })
+
+ client
+ .upgrade({ path: '/', method: 'GET' })
+ .then(() => {
+ t.fail()
+ })
+ .catch(error => {
+ t.type(error, Error)
+ })
+ })
+})
+
+test('upgrade invalid signal', (t) => {
+ t.plan(2)
+
+ const server = net.createServer(() => {
+ t.fail()
+ })
+ t.teardown(server.close.bind(server))
+
+ server.listen(0, () => {
+ const client = new Client(`http://localhost:${server.address().port}`)
+ t.teardown(client.destroy.bind(client))
+
+ client.on('disconnect', () => {
+ t.fail()
+ })
+
+ client.upgrade({
+ path: '/',
+ method: 'GET',
+ protocol: 'Websocket',
+ signal: 'error',
+ opaque: 'asd'
+ }, (err, { opaque }) => {
+ t.equal(opaque, 'asd')
+ t.type(err, errors.InvalidArgumentError)
+ })
+ })
+})