2014-04-14 05:21:26 +04:00
|
|
|
<!DOCTYPE HTML>
|
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<title>Test UDPSocket API</title>
|
2019-04-15 23:56:58 +03:00
|
|
|
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
2016-04-21 16:48:59 +03:00
|
|
|
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
2014-04-14 05:21:26 +04:00
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<p id="display"></p>
|
|
|
|
<div id="content" style="display: none">
|
|
|
|
</div>
|
|
|
|
<iframe id="iframe"></iframe>
|
|
|
|
<pre id="test">
|
2017-02-23 00:10:07 +03:00
|
|
|
<script type="application/javascript">
|
2014-04-14 05:21:26 +04:00
|
|
|
'use strict';
|
|
|
|
SimpleTest.waitForExplicitFinish();
|
2014-12-11 21:34:40 +03:00
|
|
|
SimpleTest.requestFlakyTimeout("untriaged");
|
2014-04-14 05:21:26 +04:00
|
|
|
|
|
|
|
const HELLO_WORLD = 'hlo wrld. ';
|
|
|
|
const DATA_ARRAY = [0, 255, 254, 0, 1, 2, 3, 0, 255, 255, 254, 0];
|
|
|
|
const DATA_ARRAY_BUFFER = new ArrayBuffer(DATA_ARRAY.length);
|
|
|
|
const TYPED_DATA_ARRAY = new Uint8Array(DATA_ARRAY_BUFFER);
|
|
|
|
const BIG_ARRAY = new Array(4096);
|
|
|
|
const BIG_ARRAY_BUFFER = new ArrayBuffer(BIG_ARRAY.length);
|
|
|
|
const BIG_TYPED_ARRAY = new Uint8Array(BIG_ARRAY_BUFFER);
|
|
|
|
|
|
|
|
for (let i = 0; i < BIG_ARRAY.length; i++) {
|
|
|
|
BIG_ARRAY[i] = Math.floor(Math.random() * 256);
|
|
|
|
}
|
|
|
|
|
|
|
|
TYPED_DATA_ARRAY.set(DATA_ARRAY);
|
|
|
|
BIG_TYPED_ARRAY.set(BIG_ARRAY);
|
|
|
|
|
|
|
|
function is_same_buffer(recv_data, expect_data) {
|
|
|
|
let recv_dataview = new Uint8Array(recv_data);
|
|
|
|
let expected_dataview = new Uint8Array(expect_data);
|
|
|
|
|
|
|
|
if (recv_dataview.length !== expected_dataview.length) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let i = 0; i < recv_dataview.length; i++) {
|
|
|
|
if (recv_dataview[i] != expected_dataview[i]) {
|
|
|
|
info('discover byte differenct at ' + i);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
function testOpen() {
|
|
|
|
info('test for creating an UDP Socket');
|
|
|
|
let socket = new UDPSocket();
|
|
|
|
is(socket.localPort, null, 'expect no local port before socket opened');
|
|
|
|
is(socket.localAddress, null, 'expect no local address before socket opened');
|
|
|
|
is(socket.remotePort, null, 'expected no default remote port');
|
|
|
|
is(socket.remoteAddress, null, 'expected no default remote address');
|
|
|
|
is(socket.readyState, 'opening', 'expected ready state = opening');
|
|
|
|
is(socket.loopback, false, 'expected no loopback');
|
|
|
|
is(socket.addressReuse, true, 'expect to reuse address');
|
|
|
|
|
|
|
|
return socket.opened.then(function() {
|
|
|
|
ok(true, 'expect openedPromise to be resolved after successful socket binding');
|
|
|
|
ok(!(socket.localPort === 0), 'expect allocated a local port');
|
|
|
|
is(socket.localAddress, '0.0.0.0', 'expect assigned to default address');
|
|
|
|
is(socket.readyState, 'open', 'expected ready state = open');
|
|
|
|
|
|
|
|
return socket;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function testSendString(socket) {
|
|
|
|
info('test for sending string data');
|
|
|
|
|
|
|
|
socket.send(HELLO_WORLD, '127.0.0.1', socket.localPort);
|
|
|
|
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
socket.addEventListener('message', function(msg) {
|
|
|
|
let recvData= String.fromCharCode.apply(null, new Uint8Array(msg.data));
|
|
|
|
is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
|
|
|
|
is(recvData, HELLO_WORLD, 'expected same string data');
|
|
|
|
resolve(socket);
|
|
|
|
}, {once: true});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function testSendArrayBuffer(socket) {
|
|
|
|
info('test for sending ArrayBuffer');
|
|
|
|
|
|
|
|
socket.send(DATA_ARRAY_BUFFER, '127.0.0.1', socket.localPort);
|
|
|
|
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
socket.addEventListener('message', function(msg) {
|
|
|
|
is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
|
|
|
|
ok(is_same_buffer(msg.data, DATA_ARRAY_BUFFER), 'expected same buffer data');
|
|
|
|
resolve(socket);
|
|
|
|
}, {once: true});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function testSendArrayBufferView(socket) {
|
|
|
|
info('test for sending ArrayBufferView');
|
|
|
|
|
|
|
|
socket.send(TYPED_DATA_ARRAY, '127.0.0.1', socket.localPort);
|
|
|
|
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
socket.addEventListener('message', function(msg) {
|
|
|
|
is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
|
|
|
|
ok(is_same_buffer(msg.data, TYPED_DATA_ARRAY), 'expected same buffer data');
|
|
|
|
resolve(socket);
|
|
|
|
}, {once: true});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function testSendBlob(socket) {
|
|
|
|
info('test for sending Blob');
|
|
|
|
|
|
|
|
let blob = new Blob([HELLO_WORLD], {type : 'text/plain'});
|
|
|
|
socket.send(blob, '127.0.0.1', socket.localPort);
|
|
|
|
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
socket.addEventListener('message', function(msg) {
|
|
|
|
let recvData= String.fromCharCode.apply(null, new Uint8Array(msg.data));
|
|
|
|
is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
|
|
|
|
is(recvData, HELLO_WORLD, 'expected same string data');
|
|
|
|
resolve(socket);
|
|
|
|
}, {once: true});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function testSendBigArray(socket) {
|
|
|
|
info('test for sending Big ArrayBuffer');
|
|
|
|
|
|
|
|
socket.send(BIG_TYPED_ARRAY, '127.0.0.1', socket.localPort);
|
|
|
|
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
let byteReceived = 0;
|
|
|
|
socket.addEventListener('message', function recv_callback(msg) {
|
|
|
|
let byteBegin = byteReceived;
|
|
|
|
byteReceived += msg.data.byteLength;
|
|
|
|
is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
|
|
|
|
ok(is_same_buffer(msg.data, BIG_TYPED_ARRAY.subarray(byteBegin, byteReceived)), 'expected same buffer data [' + byteBegin+ '-' + byteReceived + ']');
|
|
|
|
if (byteReceived >= BIG_TYPED_ARRAY.length) {
|
|
|
|
socket.removeEventListener('message', recv_callback);
|
|
|
|
resolve(socket);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function testSendBigBlob(socket) {
|
|
|
|
info('test for sending Big Blob');
|
|
|
|
|
|
|
|
let blob = new Blob([BIG_TYPED_ARRAY]);
|
|
|
|
socket.send(blob, '127.0.0.1', socket.localPort);
|
|
|
|
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
let byteReceived = 0;
|
|
|
|
socket.addEventListener('message', function recv_callback(msg) {
|
|
|
|
let byteBegin = byteReceived;
|
|
|
|
byteReceived += msg.data.byteLength;
|
|
|
|
is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
|
|
|
|
ok(is_same_buffer(msg.data, BIG_TYPED_ARRAY.subarray(byteBegin, byteReceived)), 'expected same buffer data [' + byteBegin+ '-' + byteReceived + ']');
|
|
|
|
if (byteReceived >= BIG_TYPED_ARRAY.length) {
|
|
|
|
socket.removeEventListener('message', recv_callback);
|
|
|
|
resolve(socket);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function testUDPOptions(socket) {
|
|
|
|
info('test for UDP init options');
|
|
|
|
|
|
|
|
let remoteSocket = new UDPSocket({addressReuse: false,
|
|
|
|
loopback: true,
|
|
|
|
localAddress: '127.0.0.1',
|
|
|
|
remoteAddress: '127.0.0.1',
|
|
|
|
remotePort: socket.localPort});
|
|
|
|
is(remoteSocket.localAddress, '127.0.0.1', 'expected local address');
|
|
|
|
is(remoteSocket.remoteAddress, '127.0.0.1', 'expected remote address');
|
|
|
|
is(remoteSocket.remotePort, socket.localPort, 'expected remote port');
|
|
|
|
is(remoteSocket.addressReuse, false, 'expected address not reusable');
|
|
|
|
is(remoteSocket.loopback, true, 'expected loopback mode is on');
|
|
|
|
|
|
|
|
return remoteSocket.opened.then(function() {
|
|
|
|
remoteSocket.send(HELLO_WORLD);
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
socket.addEventListener('message', function(msg) {
|
|
|
|
let recvData= String.fromCharCode.apply(null, new Uint8Array(msg.data));
|
|
|
|
is(msg.remotePort, remoteSocket.localPort, 'expected packet from ' + remoteSocket.localPort);
|
|
|
|
is(recvData, HELLO_WORLD, 'expected same string data');
|
|
|
|
resolve(socket);
|
|
|
|
}, {once: true});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function testClose(socket) {
|
|
|
|
info('test for close');
|
|
|
|
|
|
|
|
socket.close();
|
|
|
|
is(socket.readyState, 'closed', 'expect ready state to be "closed"');
|
|
|
|
try {
|
|
|
|
socket.send(HELLO_WORLD, '127.0.0.1', socket.localPort);
|
|
|
|
ok(false, 'unexpect to send successfully');
|
|
|
|
} catch (e) {
|
|
|
|
ok(true, 'expected send fail after socket closed');
|
|
|
|
}
|
|
|
|
|
|
|
|
return socket.closed.then(function() {
|
|
|
|
ok(true, 'expected closedPromise is resolved after socket.close()');
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function testMulticast() {
|
|
|
|
info('test for multicast');
|
|
|
|
|
|
|
|
let socket = new UDPSocket({loopback: true});
|
|
|
|
|
|
|
|
const MCAST_ADDRESS = '224.0.0.255';
|
|
|
|
socket.joinMulticastGroup(MCAST_ADDRESS);
|
|
|
|
|
|
|
|
return socket.opened.then(function() {
|
|
|
|
socket.send(HELLO_WORLD, MCAST_ADDRESS, socket.localPort);
|
|
|
|
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
socket.addEventListener('message', function(msg) {
|
|
|
|
let recvData= String.fromCharCode.apply(null, new Uint8Array(msg.data));
|
|
|
|
is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
|
|
|
|
is(recvData, HELLO_WORLD, 'expected same string data');
|
|
|
|
socket.leaveMulticastGroup(MCAST_ADDRESS);
|
|
|
|
resolve();
|
|
|
|
}, {once: true});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function testInvalidUDPOptions() {
|
|
|
|
info('test for invalid UDPOptions');
|
|
|
|
try {
|
|
|
|
let socket = new UDPSocket({localAddress: 'not-a-valid-address'});
|
|
|
|
ok(false, 'should not create an UDPSocket with an invalid localAddress');
|
|
|
|
} catch (e) {
|
|
|
|
is(e.name, 'InvalidAccessError', 'expected InvalidAccessError will be thrown if localAddress is not a valid IPv4/6 address');
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
let socket = new UDPSocket({localPort: 0});
|
|
|
|
ok(false, 'should not create an UDPSocket with an invalid localPort');
|
|
|
|
} catch (e) {
|
|
|
|
is(e.name, 'InvalidAccessError', 'expected InvalidAccessError will be thrown if localPort is not a valid port number');
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
let socket = new UDPSocket({remotePort: 0});
|
|
|
|
ok(false, 'should not create an UDPSocket with an invalid remotePort');
|
|
|
|
} catch (e) {
|
|
|
|
is(e.name, 'InvalidAccessError', 'expected InvalidAccessError will be thrown if localPort is not a valid port number');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function testOpenFailed() {
|
|
|
|
info('test for falied on open');
|
|
|
|
|
|
|
|
//according to RFC5737, address block 192.0.2.0/24 should not be used in both local and public contexts
|
|
|
|
let socket = new UDPSocket({localAddress: '192.0.2.0'});
|
|
|
|
|
|
|
|
return socket.opened.then(function() {
|
|
|
|
ok(false, 'should not resolve openedPromise while fail to bind socket');
|
2015-03-24 03:06:00 +03:00
|
|
|
socket.close();
|
2014-04-14 05:21:26 +04:00
|
|
|
}).catch(function(reason) {
|
|
|
|
is(reason.name, 'NetworkError', 'expected openedPromise to be rejected while fail to bind socket');
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function testSendBeforeOpen() {
|
|
|
|
info('test for send before open');
|
|
|
|
|
|
|
|
let socket = new UDPSocket();
|
|
|
|
|
|
|
|
try {
|
|
|
|
socket.send(HELLO_WORLD, '127.0.0.1', 9);
|
|
|
|
ok(false, 'unexpect to send successfully');
|
|
|
|
} catch (e) {
|
|
|
|
ok(true, 'expected send fail before openedPromise is resolved');
|
|
|
|
}
|
|
|
|
|
2015-03-24 03:06:00 +03:00
|
|
|
return socket.opened.then(function() {
|
|
|
|
socket.close();
|
|
|
|
});
|
2014-04-14 05:21:26 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
function testCloseBeforeOpened() {
|
|
|
|
info('test for close socket before opened');
|
|
|
|
|
|
|
|
let socket = new UDPSocket();
|
|
|
|
socket.opened.then(function() {
|
|
|
|
ok(false, 'should not resolve openedPromise if it has already been closed');
|
|
|
|
}).catch(function(reason) {
|
|
|
|
is(reason.name, 'AbortError', 'expected openedPromise to be rejected while socket is closed during opening');
|
|
|
|
});
|
|
|
|
|
|
|
|
return socket.close().then(function() {
|
|
|
|
ok(true, 'expected closedPromise to be resolved');
|
|
|
|
}).then(socket.opened);
|
|
|
|
}
|
|
|
|
|
|
|
|
function testOpenWithoutClose() {
|
|
|
|
info('test for open without close');
|
|
|
|
|
2015-03-19 13:21:32 +03:00
|
|
|
let closed = [];
|
2014-04-14 05:21:26 +04:00
|
|
|
for (let i = 0; i < 50; i++) {
|
|
|
|
let socket = new UDPSocket();
|
2015-03-19 13:21:32 +03:00
|
|
|
closed.push(socket.closed);
|
2014-04-14 05:21:26 +04:00
|
|
|
}
|
|
|
|
|
2015-04-02 09:57:29 +03:00
|
|
|
SpecialPowers.gc();
|
|
|
|
info('all unrefereced socket should be closed right after GC');
|
|
|
|
|
2015-03-19 13:21:32 +03:00
|
|
|
return Promise.all(closed);
|
2014-04-14 05:21:26 +04:00
|
|
|
}
|
|
|
|
|
2020-04-21 01:31:29 +03:00
|
|
|
function awaitEvent(target, event) {
|
|
|
|
return new Promise(resolve => {
|
|
|
|
target.addEventListener(event, resolve, { once: true });
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
async function testBFCache() {
|
2014-04-14 05:21:26 +04:00
|
|
|
info('test for bfcache behavior');
|
|
|
|
|
|
|
|
let socket = new UDPSocket();
|
|
|
|
|
2020-04-21 01:31:29 +03:00
|
|
|
await socket.opened;
|
2020-04-20 23:11:43 +03:00
|
|
|
|
2020-04-21 01:31:29 +03:00
|
|
|
let win = window.open(`file_udpsocket_iframe.html?${socket.localPort}`);
|
|
|
|
|
|
|
|
let msg = await awaitEvent(socket, "message");
|
|
|
|
|
|
|
|
win.location = "file_postMessage_opener.html";
|
|
|
|
await awaitEvent(window, "message");
|
|
|
|
|
|
|
|
socket.send(HELLO_WORLD, '127.0.0.1', msg.remotePort);
|
|
|
|
|
|
|
|
await new Promise(resolve => {
|
|
|
|
function recv_again_callback(message) {
|
|
|
|
socket.removeEventListener('message', recv_again_callback);
|
|
|
|
ok(false, 'should not receive packet after page unload');
|
|
|
|
}
|
|
|
|
|
|
|
|
socket.addEventListener('message', recv_again_callback);
|
|
|
|
|
|
|
|
let timeout = setTimeout(function() {
|
|
|
|
socket.removeEventListener('message', recv_again_callback);
|
|
|
|
socket.close();
|
|
|
|
resolve();
|
|
|
|
}, 5000);
|
2014-04-14 05:21:26 +04:00
|
|
|
});
|
2020-04-21 01:31:29 +03:00
|
|
|
|
|
|
|
win.close();
|
2014-04-14 05:21:26 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
function runTest() {
|
|
|
|
testOpen()
|
|
|
|
.then(testSendString)
|
|
|
|
.then(testSendArrayBuffer)
|
|
|
|
.then(testSendArrayBufferView)
|
|
|
|
.then(testSendBlob)
|
|
|
|
.then(testSendBigArray)
|
|
|
|
.then(testSendBigBlob)
|
|
|
|
.then(testUDPOptions)
|
|
|
|
.then(testClose)
|
|
|
|
.then(testMulticast)
|
|
|
|
.then(testInvalidUDPOptions)
|
|
|
|
.then(testOpenFailed)
|
|
|
|
.then(testSendBeforeOpen)
|
|
|
|
.then(testCloseBeforeOpened)
|
|
|
|
.then(testOpenWithoutClose)
|
|
|
|
.then(testBFCache)
|
|
|
|
.then(function() {
|
|
|
|
info('test finished');
|
|
|
|
SimpleTest.finish();
|
2015-03-19 13:21:32 +03:00
|
|
|
})
|
|
|
|
.catch(function(err) {
|
|
|
|
ok(false, 'test failed due to: ' + err);
|
|
|
|
SimpleTest.finish();
|
2014-04-14 05:21:26 +04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
window.addEventListener('load', function () {
|
2016-04-21 16:48:59 +03:00
|
|
|
SpecialPowers.pushPrefEnv({
|
|
|
|
'set': [
|
|
|
|
['dom.udpsocket.enabled', true],
|
|
|
|
['browser.sessionhistory.max_total_viewers', 10]
|
|
|
|
]
|
|
|
|
}, runTest);
|
2014-04-14 05:21:26 +04:00
|
|
|
});
|
|
|
|
|
|
|
|
</script>
|
|
|
|
</pre>
|
|
|
|
</body>
|
|
|
|
</html>
|