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