Source code

Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "TestCommon.h"
#include "gtest/gtest.h"
#include "mozilla/gtest/MozAssertions.h"
#include "nsISocketTransportService.h"
#include "nsISocketTransport.h"
#include "nsIServerSocket.h"
#include "nsIAsyncInputStream.h"
#include "mozilla/net/DNS.h"
#include "prerror.h"
#include "../../base/nsSocketTransportService2.h"
#include "nsComponentManagerUtils.h"
#include "nsServiceManagerUtils.h"
using namespace mozilla::net;
using namespace mozilla;
class ServerListener : public nsIServerSocketListener {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISERVERSOCKETLISTENER
explicit ServerListener(WaitForCondition* waiter);
// Port that is got from server side will be store here.
uint32_t mClientPort;
bool mFailed;
RefPtr<WaitForCondition> mWaiter;
private:
virtual ~ServerListener();
};
NS_IMPL_ISUPPORTS(ServerListener, nsIServerSocketListener)
ServerListener::ServerListener(WaitForCondition* waiter)
: mClientPort(-1), mFailed(false), mWaiter(waiter) {}
ServerListener::~ServerListener() = default;
NS_IMETHODIMP
ServerListener::OnSocketAccepted(nsIServerSocket* aServ,
nsISocketTransport* aTransport) {
// Run on STS thread.
NetAddr peerAddr;
nsresult rv = aTransport->GetPeerAddr(&peerAddr);
if (NS_FAILED(rv)) {
mFailed = true;
mWaiter->Notify();
return NS_OK;
}
mClientPort = PR_ntohs(peerAddr.inet.port);
mWaiter->Notify();
return NS_OK;
}
NS_IMETHODIMP
ServerListener::OnStopListening(nsIServerSocket* aServ, nsresult aStatus) {
return NS_OK;
}
class ClientInputCallback : public nsIInputStreamCallback {
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIINPUTSTREAMCALLBACK
explicit ClientInputCallback(WaitForCondition* waiter);
bool mFailed;
RefPtr<WaitForCondition> mWaiter;
private:
virtual ~ClientInputCallback();
};
NS_IMPL_ISUPPORTS(ClientInputCallback, nsIInputStreamCallback)
ClientInputCallback::ClientInputCallback(WaitForCondition* waiter)
: mFailed(false), mWaiter(waiter) {}
ClientInputCallback::~ClientInputCallback() = default;
NS_IMETHODIMP
ClientInputCallback::OnInputStreamReady(nsIAsyncInputStream* aStream) {
// Server doesn't send. That means if we are here, we probably have run into
// an error.
uint64_t avail;
nsresult rv = aStream->Available(&avail);
if (NS_FAILED(rv)) {
mFailed = true;
}
mWaiter->Notify();
return NS_OK;
}
TEST(TestBind, MainTest)
{
//
// Server side.
//
nsCOMPtr<nsIServerSocket> server =
do_CreateInstance("@mozilla.org/network/server-socket;1");
ASSERT_TRUE(server);
nsresult rv = server->Init(-1, true, -1);
ASSERT_NS_SUCCEEDED(rv);
int32_t serverPort;
rv = server->GetPort(&serverPort);
ASSERT_NS_SUCCEEDED(rv);
RefPtr<WaitForCondition> waiter = new WaitForCondition();
// Listening.
RefPtr<ServerListener> serverListener = new ServerListener(waiter);
rv = server->AsyncListen(serverListener);
ASSERT_NS_SUCCEEDED(rv);
//
// Client side
//
uint32_t bindingPort = 20000;
nsCOMPtr<nsISocketTransportService> service =
do_GetService("@mozilla.org/network/socket-transport-service;1", &rv);
ASSERT_NS_SUCCEEDED(rv);
nsCOMPtr<nsIInputStream> inputStream;
RefPtr<ClientInputCallback> clientCallback;
auto* sts = gSocketTransportService;
ASSERT_TRUE(sts);
for (int32_t tried = 0; tried < 100; tried++) {
NS_DispatchAndSpinEventLoopUntilComplete(
"test"_ns, sts, NS_NewRunnableFunction("test", [&]() {
nsCOMPtr<nsISocketTransport> client;
rv = service->CreateTransport(nsTArray<nsCString>(), "127.0.0.1"_ns,
serverPort, nullptr, nullptr,
getter_AddRefs(client));
ASSERT_NS_SUCCEEDED(rv);
// Bind to a port. It's possible that we are binding to a port
// that is currently in use. If we failed to bind, we try next
// port.
NetAddr bindingAddr;
bindingAddr.inet.family = AF_INET;
bindingAddr.inet.ip = 0;
bindingAddr.inet.port = PR_htons(bindingPort);
rv = client->Bind(&bindingAddr);
ASSERT_NS_SUCCEEDED(rv);
// Open IO streams, to make client SocketTransport connect to
// server.
clientCallback = new ClientInputCallback(waiter);
rv = client->OpenInputStream(nsITransport::OPEN_UNBUFFERED, 0, 0,
getter_AddRefs(inputStream));
ASSERT_NS_SUCCEEDED(rv);
nsCOMPtr<nsIAsyncInputStream> asyncInputStream =
do_QueryInterface(inputStream);
rv = asyncInputStream->AsyncWait(clientCallback, 0, 0, nullptr);
}));
// Wait for server's response or callback of input stream.
waiter->Wait(1);
if (clientCallback->mFailed) {
// if client received error, we likely have bound a port that is
// in use. we can try another port.
bindingPort++;
} else {
// We are unlocked by server side, leave the loop and check
// result.
break;
}
}
ASSERT_FALSE(serverListener->mFailed);
ASSERT_EQ(serverListener->mClientPort, bindingPort);
inputStream->Close();
waiter->Wait(1);
ASSERT_TRUE(clientCallback->mFailed);
server->Close();
}