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