TestBind.cpp (5490B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "TestCommon.h" 6 #include "gtest/gtest.h" 7 #include "mozilla/gtest/MozAssertions.h" 8 #include "nsISocketTransportService.h" 9 #include "nsISocketTransport.h" 10 #include "nsIServerSocket.h" 11 #include "nsIAsyncInputStream.h" 12 #include "mozilla/net/DNS.h" 13 #include "prerror.h" 14 #include "../../base/nsSocketTransportService2.h" 15 #include "nsComponentManagerUtils.h" 16 #include "nsServiceManagerUtils.h" 17 18 using namespace mozilla::net; 19 using namespace mozilla; 20 21 class ServerListener : public nsIServerSocketListener { 22 public: 23 NS_DECL_ISUPPORTS 24 NS_DECL_NSISERVERSOCKETLISTENER 25 26 explicit ServerListener(WaitForCondition* waiter); 27 28 // Port that is got from server side will be store here. 29 uint32_t mClientPort; 30 bool mFailed; 31 RefPtr<WaitForCondition> mWaiter; 32 33 private: 34 virtual ~ServerListener(); 35 }; 36 37 NS_IMPL_ISUPPORTS(ServerListener, nsIServerSocketListener) 38 39 ServerListener::ServerListener(WaitForCondition* waiter) 40 : mClientPort(-1), mFailed(false), mWaiter(waiter) {} 41 42 ServerListener::~ServerListener() = default; 43 44 NS_IMETHODIMP 45 ServerListener::OnSocketAccepted(nsIServerSocket* aServ, 46 nsISocketTransport* aTransport) { 47 // Run on STS thread. 48 NetAddr peerAddr; 49 nsresult rv = aTransport->GetPeerAddr(&peerAddr); 50 if (NS_FAILED(rv)) { 51 mFailed = true; 52 mWaiter->Notify(); 53 return NS_OK; 54 } 55 mClientPort = PR_ntohs(peerAddr.inet.port); 56 mWaiter->Notify(); 57 return NS_OK; 58 } 59 60 NS_IMETHODIMP 61 ServerListener::OnStopListening(nsIServerSocket* aServ, nsresult aStatus) { 62 return NS_OK; 63 } 64 65 class ClientInputCallback : public nsIInputStreamCallback { 66 public: 67 NS_DECL_THREADSAFE_ISUPPORTS 68 NS_DECL_NSIINPUTSTREAMCALLBACK 69 70 explicit ClientInputCallback(WaitForCondition* waiter); 71 72 bool mFailed; 73 RefPtr<WaitForCondition> mWaiter; 74 75 private: 76 virtual ~ClientInputCallback(); 77 }; 78 79 NS_IMPL_ISUPPORTS(ClientInputCallback, nsIInputStreamCallback) 80 81 ClientInputCallback::ClientInputCallback(WaitForCondition* waiter) 82 : mFailed(false), mWaiter(waiter) {} 83 84 ClientInputCallback::~ClientInputCallback() = default; 85 86 NS_IMETHODIMP 87 ClientInputCallback::OnInputStreamReady(nsIAsyncInputStream* aStream) { 88 // Server doesn't send. That means if we are here, we probably have run into 89 // an error. 90 uint64_t avail; 91 nsresult rv = aStream->Available(&avail); 92 if (NS_FAILED(rv)) { 93 mFailed = true; 94 } 95 mWaiter->Notify(); 96 return NS_OK; 97 } 98 99 TEST(TestBind, MainTest) 100 { 101 // 102 // Server side. 103 // 104 nsCOMPtr<nsIServerSocket> server = 105 do_CreateInstance("@mozilla.org/network/server-socket;1"); 106 ASSERT_TRUE(server); 107 108 nsresult rv = server->Init(-1, true, -1); 109 ASSERT_NS_SUCCEEDED(rv); 110 111 int32_t serverPort; 112 rv = server->GetPort(&serverPort); 113 ASSERT_NS_SUCCEEDED(rv); 114 115 RefPtr<WaitForCondition> waiter = new WaitForCondition(); 116 117 // Listening. 118 RefPtr<ServerListener> serverListener = new ServerListener(waiter); 119 rv = server->AsyncListen(serverListener); 120 ASSERT_NS_SUCCEEDED(rv); 121 122 // 123 // Client side 124 // 125 uint32_t bindingPort = 20000; 126 nsCOMPtr<nsISocketTransportService> service = 127 do_GetService("@mozilla.org/network/socket-transport-service;1", &rv); 128 ASSERT_NS_SUCCEEDED(rv); 129 130 nsCOMPtr<nsIInputStream> inputStream; 131 RefPtr<ClientInputCallback> clientCallback; 132 133 auto* sts = gSocketTransportService; 134 ASSERT_TRUE(sts); 135 for (int32_t tried = 0; tried < 100; tried++) { 136 NS_DispatchAndSpinEventLoopUntilComplete( 137 "test"_ns, sts, NS_NewRunnableFunction("test", [&]() { 138 nsCOMPtr<nsISocketTransport> client; 139 rv = service->CreateTransport(nsTArray<nsCString>(), "127.0.0.1"_ns, 140 serverPort, nullptr, nullptr, 141 getter_AddRefs(client)); 142 ASSERT_NS_SUCCEEDED(rv); 143 144 // Bind to a port. It's possible that we are binding to a port 145 // that is currently in use. If we failed to bind, we try next 146 // port. 147 NetAddr bindingAddr; 148 bindingAddr.inet.family = AF_INET; 149 bindingAddr.inet.ip = 0; 150 bindingAddr.inet.port = PR_htons(bindingPort); 151 rv = client->Bind(&bindingAddr); 152 ASSERT_NS_SUCCEEDED(rv); 153 154 // Open IO streams, to make client SocketTransport connect to 155 // server. 156 clientCallback = new ClientInputCallback(waiter); 157 rv = client->OpenInputStream(nsITransport::OPEN_UNBUFFERED, 0, 0, 158 getter_AddRefs(inputStream)); 159 ASSERT_NS_SUCCEEDED(rv); 160 161 nsCOMPtr<nsIAsyncInputStream> asyncInputStream = 162 do_QueryInterface(inputStream); 163 rv = asyncInputStream->AsyncWait(clientCallback, 0, 0, nullptr); 164 })); 165 166 // Wait for server's response or callback of input stream. 167 waiter->Wait(1); 168 if (clientCallback->mFailed) { 169 // if client received error, we likely have bound a port that is 170 // in use. we can try another port. 171 bindingPort++; 172 } else { 173 // We are unlocked by server side, leave the loop and check 174 // result. 175 break; 176 } 177 } 178 179 ASSERT_FALSE(serverListener->mFailed); 180 ASSERT_EQ(serverListener->mClientPort, bindingPort); 181 182 inputStream->Close(); 183 waiter->Wait(1); 184 ASSERT_TRUE(clientCallback->mFailed); 185 186 server->Close(); 187 }