зеркало из https://github.com/mozilla/gecko-dev.git
Bug 960397 - Part 2: Multicast option support for UDPSocket. r=mayhemer
This commit is contained in:
Родитель
4df91c74b0
Коммит
58bef3b4fb
|
@ -64,6 +64,26 @@ ResolveHost(const nsACString &host, nsIDNSListener *listener)
|
|||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class SetSocketOptionRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
SetSocketOptionRunnable(nsUDPSocket* aSocket, const PRSocketOptionData& aOpt)
|
||||
: mSocket(aSocket)
|
||||
, mOpt(aOpt)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
return mSocket->SetSocketOption(mOpt);
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<nsUDPSocket> mSocket;
|
||||
PRSocketOptionData mOpt;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsUDPOutputStream impl
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -924,3 +944,248 @@ nsUDPSocket::SendWithAddress(const NetAddr *aAddr, const uint8_t *aData,
|
|||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUDPSocket::SetSocketOption(const PRSocketOptionData& aOpt)
|
||||
{
|
||||
bool onSTSThread = false;
|
||||
mSts->IsOnCurrentThread(&onSTSThread);
|
||||
|
||||
if (!onSTSThread) {
|
||||
// Dispatch to STS thread and re-enter this method there
|
||||
nsCOMPtr<nsIRunnable> runnable = new SetSocketOptionRunnable(this, aOpt);
|
||||
nsresult rv = mSts->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!mFD)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (PR_SetSocketOption(mFD, &aOpt) != PR_SUCCESS) {
|
||||
SOCKET_LOG(("nsUDPSocket::SetSocketOption [this=%p] failed for type %d, "
|
||||
"error %d\n", this, aOpt.option, PR_GetError()));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUDPSocket::JoinMulticast(const nsACString& aAddr, const nsACString& aIface)
|
||||
{
|
||||
if (NS_WARN_IF(aAddr.IsEmpty())) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (NS_WARN_IF(!mFD)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
PRNetAddr prAddr;
|
||||
if (PR_StringToNetAddr(aAddr.BeginReading(), &prAddr) != PR_SUCCESS) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
PRNetAddr prIface;
|
||||
if (aIface.IsEmpty()) {
|
||||
PR_InitializeNetAddr(PR_IpAddrAny, 0, &prIface);
|
||||
} else {
|
||||
if (PR_StringToNetAddr(aIface.BeginReading(), &prIface) != PR_SUCCESS) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return JoinMulticastInternal(prAddr, prIface);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUDPSocket::JoinMulticastAddr(const NetAddr aAddr, const NetAddr* aIface)
|
||||
{
|
||||
if (NS_WARN_IF(!mFD)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
PRNetAddr prAddr;
|
||||
NetAddrToPRNetAddr(&aAddr, &prAddr);
|
||||
|
||||
PRNetAddr prIface;
|
||||
if (!aIface) {
|
||||
PR_InitializeNetAddr(PR_IpAddrAny, 0, &prIface);
|
||||
} else {
|
||||
NetAddrToPRNetAddr(aIface, &prIface);
|
||||
}
|
||||
|
||||
return JoinMulticastInternal(prAddr, prIface);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUDPSocket::JoinMulticastInternal(const PRNetAddr& aAddr,
|
||||
const PRNetAddr& aIface)
|
||||
{
|
||||
PRSocketOptionData opt;
|
||||
|
||||
opt.option = PR_SockOpt_AddMember;
|
||||
opt.value.add_member.mcaddr = aAddr;
|
||||
opt.value.add_member.ifaddr = aIface;
|
||||
|
||||
nsresult rv = SetSocketOption(opt);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUDPSocket::LeaveMulticast(const nsACString& aAddr, const nsACString& aIface)
|
||||
{
|
||||
if (NS_WARN_IF(aAddr.IsEmpty())) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (NS_WARN_IF(!mFD)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
PRNetAddr prAddr;
|
||||
if (PR_StringToNetAddr(aAddr.BeginReading(), &prAddr) != PR_SUCCESS) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
PRNetAddr prIface;
|
||||
if (aIface.IsEmpty()) {
|
||||
PR_InitializeNetAddr(PR_IpAddrAny, 0, &prIface);
|
||||
} else {
|
||||
if (PR_StringToNetAddr(aIface.BeginReading(), &prIface) != PR_SUCCESS) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return LeaveMulticastInternal(prAddr, prIface);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUDPSocket::LeaveMulticastAddr(const NetAddr aAddr, const NetAddr* aIface)
|
||||
{
|
||||
if (NS_WARN_IF(!mFD)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
PRNetAddr prAddr;
|
||||
NetAddrToPRNetAddr(&aAddr, &prAddr);
|
||||
|
||||
PRNetAddr prIface;
|
||||
if (!aIface) {
|
||||
PR_InitializeNetAddr(PR_IpAddrAny, 0, &prIface);
|
||||
} else {
|
||||
NetAddrToPRNetAddr(aIface, &prIface);
|
||||
}
|
||||
|
||||
return LeaveMulticastInternal(prAddr, prIface);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUDPSocket::LeaveMulticastInternal(const PRNetAddr& aAddr,
|
||||
const PRNetAddr& aIface)
|
||||
{
|
||||
PRSocketOptionData opt;
|
||||
|
||||
opt.option = PR_SockOpt_DropMember;
|
||||
opt.value.drop_member.mcaddr = aAddr;
|
||||
opt.value.drop_member.ifaddr = aIface;
|
||||
|
||||
nsresult rv = SetSocketOption(opt);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUDPSocket::GetMulticastLoopback(bool* aLoopback)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUDPSocket::SetMulticastLoopback(bool aLoopback)
|
||||
{
|
||||
if (NS_WARN_IF(!mFD)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
PRSocketOptionData opt;
|
||||
|
||||
opt.option = PR_SockOpt_McastLoopback;
|
||||
opt.value.mcast_loopback = aLoopback;
|
||||
|
||||
nsresult rv = SetSocketOption(opt);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUDPSocket::GetMulticastInterface(nsACString& aIface)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUDPSocket::GetMulticastInterfaceAddr(NetAddr* aIface)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUDPSocket::SetMulticastInterface(const nsACString& aIface)
|
||||
{
|
||||
if (NS_WARN_IF(!mFD)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
PRNetAddr prIface;
|
||||
if (aIface.IsEmpty()) {
|
||||
PR_InitializeNetAddr(PR_IpAddrAny, 0, &prIface);
|
||||
} else {
|
||||
if (PR_StringToNetAddr(aIface.BeginReading(), &prIface) != PR_SUCCESS) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return SetMulticastInterfaceInternal(prIface);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUDPSocket::SetMulticastInterfaceAddr(NetAddr aIface)
|
||||
{
|
||||
if (NS_WARN_IF(!mFD)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
PRNetAddr prIface;
|
||||
NetAddrToPRNetAddr(&aIface, &prIface);
|
||||
|
||||
return SetMulticastInterfaceInternal(prIface);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUDPSocket::SetMulticastInterfaceInternal(const PRNetAddr& aIface)
|
||||
{
|
||||
PRSocketOptionData opt;
|
||||
|
||||
opt.option = PR_SockOpt_McastInterface;
|
||||
opt.value.mcast_if = aIface;
|
||||
|
||||
nsresult rv = SetSocketOption(opt);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,14 @@ private:
|
|||
// try attaching our socket (mFD) to the STS's poll list.
|
||||
nsresult TryAttach();
|
||||
|
||||
friend class SetSocketOptionRunnable;
|
||||
nsresult SetSocketOption(const PRSocketOptionData& aOpt);
|
||||
nsresult JoinMulticastInternal(const PRNetAddr& aAddr,
|
||||
const PRNetAddr& aIface);
|
||||
nsresult LeaveMulticastInternal(const PRNetAddr& aAddr,
|
||||
const PRNetAddr& aIface);
|
||||
nsresult SetMulticastInterfaceInternal(const PRNetAddr& aIface);
|
||||
|
||||
// lock protects access to mListener;
|
||||
// so mListener is not cleared while being used/locked.
|
||||
mozilla::Mutex mLock;
|
||||
|
|
|
@ -10,11 +10,13 @@
|
|||
#include "nsIOutputStream.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsINetAddr.h"
|
||||
#include "nsITimer.h"
|
||||
#include "mozilla/net/DNS.h"
|
||||
#include "prerror.h"
|
||||
|
||||
#define REQUEST 0x68656c6f
|
||||
#define RESPONSE 0x6f6c6568
|
||||
#define MULTICAST_TIMEOUT 2000
|
||||
|
||||
#define EXPECT_SUCCESS(rv, ...) \
|
||||
PR_BEGIN_MACRO \
|
||||
|
@ -44,6 +46,7 @@
|
|||
enum TestPhase {
|
||||
TEST_OUTPUT_STREAM,
|
||||
TEST_SEND_API,
|
||||
TEST_MULTICAST,
|
||||
TEST_NONE
|
||||
};
|
||||
|
||||
|
@ -195,6 +198,8 @@ UDPServerListener::OnPacketReceived(nsIUDPSocket* socket, nsIUDPMessage* message
|
|||
fail("Response written");
|
||||
}
|
||||
return NS_OK;
|
||||
} else if (TEST_MULTICAST == phase && CheckMessageContent(message, REQUEST)) {
|
||||
mResult = NS_OK;
|
||||
} else if (TEST_SEND_API != phase || !CheckMessageContent(message, RESPONSE)) {
|
||||
mResult = NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -211,6 +216,39 @@ UDPServerListener::OnStopListening(nsIUDPSocket*, nsresult)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multicast timer callback: detects delivery failure
|
||||
*/
|
||||
class MulticastTimerCallback : public nsITimerCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSITIMERCALLBACK
|
||||
|
||||
virtual ~MulticastTimerCallback();
|
||||
|
||||
nsresult mResult;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(MulticastTimerCallback, nsITimerCallback)
|
||||
|
||||
MulticastTimerCallback::~MulticastTimerCallback()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MulticastTimerCallback::Notify(nsITimer* timer)
|
||||
{
|
||||
if (TEST_MULTICAST != phase) {
|
||||
return NS_OK;
|
||||
}
|
||||
// Multicast ping failed
|
||||
printf("Multicast ping timeout expired\n");
|
||||
mResult = NS_ERROR_FAILURE;
|
||||
QuitPumpingEvents();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**** Main ****/
|
||||
int
|
||||
main(int32_t argc, char *argv[])
|
||||
|
@ -230,8 +268,8 @@ main(int32_t argc, char *argv[])
|
|||
// Create UDPServerListener to process UDP packets
|
||||
nsRefPtr<UDPServerListener> serverListener = new UDPServerListener();
|
||||
|
||||
// Bind server socket to 127.0.0.1
|
||||
rv = server->Init(0, true);
|
||||
// Bind server socket to 0.0.0.0
|
||||
rv = server->Init(0, false);
|
||||
NS_ENSURE_SUCCESS(rv, -1);
|
||||
int32_t serverPort;
|
||||
server->GetPort(&serverPort);
|
||||
|
@ -239,7 +277,7 @@ main(int32_t argc, char *argv[])
|
|||
|
||||
// Bind clinet on arbitrary port
|
||||
nsRefPtr<UDPClientListener> clientListener = new UDPClientListener();
|
||||
client->Init(0, true);
|
||||
client->Init(0, false);
|
||||
client->AsyncListen(clientListener);
|
||||
|
||||
// Write data to server
|
||||
|
@ -262,6 +300,9 @@ main(int32_t argc, char *argv[])
|
|||
mozilla::net::NetAddr clientAddr;
|
||||
rv = client->GetAddress(&clientAddr);
|
||||
NS_ENSURE_SUCCESS(rv, -1);
|
||||
// The client address is 0.0.0.0, but Windows won't receive packets there, so
|
||||
// use 127.0.0.1 explicitly
|
||||
clientAddr.inet.ip = PR_htonl(127 << 24 | 1);
|
||||
|
||||
phase = TEST_SEND_API;
|
||||
rv = server->SendWithAddress(&clientAddr, (uint8_t*)&data, sizeof(uint32_t), &count);
|
||||
|
@ -276,6 +317,143 @@ main(int32_t argc, char *argv[])
|
|||
// Read response from server
|
||||
NS_ENSURE_SUCCESS(clientListener->mResult, -1);
|
||||
|
||||
// Setup timer to detect multicast failure
|
||||
nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
|
||||
if (NS_WARN_IF(!timer)) {
|
||||
return -1;
|
||||
}
|
||||
nsCOMPtr<MulticastTimerCallback> timerCb = new MulticastTimerCallback();
|
||||
|
||||
// The following multicast tests using multiple sockets require a firewall
|
||||
// exception on Windows XP before they pass. For now, we'll skip them here.
|
||||
// Later versions of Windows don't seem to have this issue.
|
||||
#ifdef XP_WIN
|
||||
OSVERSIONINFO OsVersion;
|
||||
OsVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
||||
GetVersionEx(&OsVersion);
|
||||
if (OsVersion.dwMajorVersion == 5 && OsVersion.dwMinorVersion == 1) {
|
||||
goto close;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Join multicast group
|
||||
printf("Joining multicast group\n");
|
||||
phase = TEST_MULTICAST;
|
||||
mozilla::net::NetAddr multicastAddr;
|
||||
multicastAddr.inet.family = AF_INET;
|
||||
multicastAddr.inet.ip = PR_htonl(224 << 24 | 255);
|
||||
multicastAddr.inet.port = PR_htons(serverPort);
|
||||
rv = server->JoinMulticastAddr(multicastAddr, nullptr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Send multicast ping
|
||||
timerCb->mResult = NS_OK;
|
||||
timer->InitWithCallback(timerCb, MULTICAST_TIMEOUT, nsITimer::TYPE_ONE_SHOT);
|
||||
rv = client->SendWithAddress(&multicastAddr, (uint8_t*)&data, sizeof(uint32_t), &count);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return -1;
|
||||
}
|
||||
REQUIRE_EQUAL(count, sizeof(uint32_t), "Error");
|
||||
passed("Multicast ping written by SendWithAddress");
|
||||
|
||||
// Wait for server to receive successfully
|
||||
PumpEvents();
|
||||
if (NS_WARN_IF(NS_FAILED(serverListener->mResult))) {
|
||||
return -1;
|
||||
}
|
||||
if (NS_WARN_IF(NS_FAILED(timerCb->mResult))) {
|
||||
return -1;
|
||||
}
|
||||
timer->Cancel();
|
||||
passed("Server received ping successfully");
|
||||
|
||||
// Disable multicast loopback
|
||||
printf("Disable multicast loopback\n");
|
||||
client->SetMulticastLoopback(false);
|
||||
server->SetMulticastLoopback(false);
|
||||
|
||||
// Send multicast ping
|
||||
timerCb->mResult = NS_OK;
|
||||
timer->InitWithCallback(timerCb, MULTICAST_TIMEOUT, nsITimer::TYPE_ONE_SHOT);
|
||||
rv = client->SendWithAddress(&multicastAddr, (uint8_t*)&data, sizeof(uint32_t), &count);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return -1;
|
||||
}
|
||||
REQUIRE_EQUAL(count, sizeof(uint32_t), "Error");
|
||||
passed("Multicast ping written by SendWithAddress");
|
||||
|
||||
// Wait for server to fail to receive
|
||||
PumpEvents();
|
||||
if (NS_WARN_IF(NS_SUCCEEDED(timerCb->mResult))) {
|
||||
return -1;
|
||||
}
|
||||
timer->Cancel();
|
||||
passed("Server failed to receive ping correctly");
|
||||
|
||||
// Reset state
|
||||
client->SetMulticastLoopback(true);
|
||||
server->SetMulticastLoopback(true);
|
||||
|
||||
// Change multicast interface
|
||||
printf("Changing multicast interface\n");
|
||||
mozilla::net::NetAddr loopbackAddr;
|
||||
loopbackAddr.inet.family = AF_INET;
|
||||
loopbackAddr.inet.ip = PR_htonl(INADDR_LOOPBACK);
|
||||
client->SetMulticastInterfaceAddr(loopbackAddr);
|
||||
|
||||
// Send multicast ping
|
||||
timerCb->mResult = NS_OK;
|
||||
timer->InitWithCallback(timerCb, MULTICAST_TIMEOUT, nsITimer::TYPE_ONE_SHOT);
|
||||
rv = client->SendWithAddress(&multicastAddr, (uint8_t*)&data, sizeof(uint32_t), &count);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return -1;
|
||||
}
|
||||
REQUIRE_EQUAL(count, sizeof(uint32_t), "Error");
|
||||
passed("Multicast ping written by SendWithAddress");
|
||||
|
||||
// Wait for server to fail to receive
|
||||
PumpEvents();
|
||||
if (NS_WARN_IF(NS_SUCCEEDED(timerCb->mResult))) {
|
||||
return -1;
|
||||
}
|
||||
timer->Cancel();
|
||||
passed("Server failed to receive ping correctly");
|
||||
|
||||
// Reset state
|
||||
mozilla::net::NetAddr anyAddr;
|
||||
anyAddr.inet.family = AF_INET;
|
||||
anyAddr.inet.ip = PR_htonl(INADDR_ANY);
|
||||
client->SetMulticastInterfaceAddr(anyAddr);
|
||||
|
||||
// Leave multicast group
|
||||
printf("Leave multicast group\n");
|
||||
rv = server->LeaveMulticastAddr(multicastAddr, nullptr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Send multicast ping
|
||||
timerCb->mResult = NS_OK;
|
||||
timer->InitWithCallback(timerCb, MULTICAST_TIMEOUT, nsITimer::TYPE_ONE_SHOT);
|
||||
rv = client->SendWithAddress(&multicastAddr, (uint8_t*)&data, sizeof(uint32_t), &count);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return -1;
|
||||
}
|
||||
REQUIRE_EQUAL(count, sizeof(uint32_t), "Error");
|
||||
passed("Multicast ping written by SendWithAddress");
|
||||
|
||||
// Wait for server to fail to receive
|
||||
PumpEvents();
|
||||
if (NS_WARN_IF(NS_SUCCEEDED(timerCb->mResult))) {
|
||||
return -1;
|
||||
}
|
||||
timer->Cancel();
|
||||
passed("Server failed to receive ping correctly");
|
||||
goto close;
|
||||
|
||||
close:
|
||||
// Close server
|
||||
printf("*** Attempting to close server ...\n");
|
||||
server->Close();
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
// Bug 960397: UDP multicast options
|
||||
|
||||
const { Constructor: CC } = Components;
|
||||
|
||||
const UDPSocket = CC("@mozilla.org/network/udp-socket;1",
|
||||
"nsIUDPSocket",
|
||||
"init");
|
||||
const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
|
||||
const ADDRESS = "224.0.0.255";
|
||||
const TIMEOUT = 2000;
|
||||
|
||||
const ua = Cc["@mozilla.org/network/protocol;1?name=http"]
|
||||
.getService(Ci.nsIHttpProtocolHandler).userAgent;
|
||||
const isWinXP = ua.indexOf("Windows NT 5.1") != -1;
|
||||
|
||||
let gConverter;
|
||||
|
||||
function run_test() {
|
||||
setup();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function setup() {
|
||||
gConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
|
||||
createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
gConverter.charset = "utf8";
|
||||
}
|
||||
|
||||
function createSocketAndJoin() {
|
||||
let socket = new UDPSocket(-1, false);
|
||||
socket.joinMulticast(ADDRESS);
|
||||
return socket;
|
||||
}
|
||||
|
||||
function sendPing(socket) {
|
||||
let ping = "ping";
|
||||
let rawPing = gConverter.convertToByteArray(ping);
|
||||
|
||||
let deferred = promise.defer();
|
||||
|
||||
socket.asyncListen({
|
||||
onPacketReceived: function(s, message) {
|
||||
do_print("Received on port " + socket.port);
|
||||
do_check_eq(message.data, ping);
|
||||
socket.close();
|
||||
deferred.resolve(message.data);
|
||||
},
|
||||
onStopListening: function(socket, status) {}
|
||||
});
|
||||
|
||||
do_print("Multicast send to port " + socket.port);
|
||||
socket.send(ADDRESS, socket.port, rawPing, rawPing.length);
|
||||
|
||||
// Timers are bad, but it seems like the only way to test *not* getting a
|
||||
// packet.
|
||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
timer.initWithCallback(() => {
|
||||
socket.close();
|
||||
deferred.reject();
|
||||
}, TIMEOUT, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
add_test(() => {
|
||||
do_print("Joining multicast group");
|
||||
let socket = createSocketAndJoin();
|
||||
sendPing(socket).then(
|
||||
run_next_test,
|
||||
() => do_throw("Joined group, but no packet received")
|
||||
);
|
||||
});
|
||||
|
||||
add_test(() => {
|
||||
do_print("Disabling multicast loopback");
|
||||
let socket = createSocketAndJoin();
|
||||
socket.multicastLoopback = false;
|
||||
sendPing(socket).then(
|
||||
() => do_throw("Loopback disabled, but still got a packet"),
|
||||
run_next_test
|
||||
);
|
||||
});
|
||||
|
||||
// The following multicast interface test doesn't work on Windows XP, as it
|
||||
// appears to allow packets no matter what address is given, so we'll skip the
|
||||
// test there.
|
||||
if (!isWinXP) {
|
||||
add_test(() => {
|
||||
do_print("Changing multicast interface");
|
||||
let socket = createSocketAndJoin();
|
||||
socket.multicastInterface = "127.0.0.1";
|
||||
sendPing(socket).then(
|
||||
() => do_throw("Changed interface, but still got a packet"),
|
||||
run_next_test
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
add_test(() => {
|
||||
do_print("Leaving multicast group");
|
||||
let socket = createSocketAndJoin();
|
||||
socket.leaveMulticast(ADDRESS);
|
||||
sendPing(socket).then(
|
||||
() => do_throw("Left group, but still got a packet"),
|
||||
run_next_test
|
||||
);
|
||||
});
|
|
@ -320,3 +320,4 @@ skip-if = os == "android"
|
|||
skip-if = os == "android"
|
||||
[test_signature_extraction.js]
|
||||
run-if = os == "win"
|
||||
[test_udp_multicast.js]
|
||||
|
|
Загрузка…
Ссылка в новой задаче