diff options
Diffstat (limited to '')
-rw-r--r-- | netwerk/test/gtest/TestBind.cpp | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/netwerk/test/gtest/TestBind.cpp b/netwerk/test/gtest/TestBind.cpp new file mode 100644 index 0000000000..371a09fdab --- /dev/null +++ b/netwerk/test/gtest/TestBind.cpp @@ -0,0 +1,187 @@ +/* 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(); +} |