1 // Copyright 2023 gRPC authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <grpc/support/port_platform.h>
16
17 #ifdef GPR_APPLE
18
19 #include <thread>
20
21 #include "absl/status/status.h"
22 #include "absl/strings/str_format.h"
23 #include "gmock/gmock.h"
24 #include "gtest/gtest.h"
25
26 #include <grpc/event_engine/event_engine.h>
27 #include <grpc/grpc.h>
28
29 #include "src/core/lib/event_engine/cf_engine/cf_engine.h"
30 #include "src/core/lib/event_engine/channel_args_endpoint_config.h"
31 #include "src/core/lib/event_engine/tcp_socket_utils.h"
32 #include "src/core/lib/resource_quota/memory_quota.h"
33 #include "src/core/lib/resource_quota/resource_quota.h"
34 #include "test/core/util/port.h"
35
36 using namespace std::chrono_literals;
37
38 namespace grpc_event_engine {
39 namespace experimental {
40
TEST(CFEventEngineTest,TestConnectionTimeout)41 TEST(CFEventEngineTest, TestConnectionTimeout) {
42 // use a non-routable IP so connection will timeout
43 auto resolved_addr = URIToResolvedAddress("ipv4:10.255.255.255:1234");
44 GPR_ASSERT(resolved_addr.ok());
45
46 grpc_core::MemoryQuota memory_quota("cf_engine_test");
47 grpc_core::Notification client_signal;
48 auto cf_engine = std::make_shared<CFEventEngine>();
49
50 ChannelArgsEndpointConfig config(grpc_core::ChannelArgs().Set(
51 GRPC_ARG_RESOURCE_QUOTA, grpc_core::ResourceQuota::Default()));
52 cf_engine->Connect(
53 [&client_signal](auto endpoint) {
54 EXPECT_EQ(endpoint.status().code(),
55 absl::StatusCode::kDeadlineExceeded);
56 client_signal.Notify();
57 },
58 *resolved_addr, config, memory_quota.CreateMemoryAllocator("conn1"), 1ms);
59
60 client_signal.WaitForNotification();
61 }
62
TEST(CFEventEngineTest,TestConnectionCancelled)63 TEST(CFEventEngineTest, TestConnectionCancelled) {
64 // use a non-routable IP so to cancel connection before timeout
65 auto resolved_addr = URIToResolvedAddress("ipv4:10.255.255.255:1234");
66 GPR_ASSERT(resolved_addr.ok());
67
68 grpc_core::MemoryQuota memory_quota("cf_engine_test");
69 grpc_core::Notification client_signal;
70 auto cf_engine = std::make_shared<CFEventEngine>();
71
72 ChannelArgsEndpointConfig config(grpc_core::ChannelArgs().Set(
73 GRPC_ARG_RESOURCE_QUOTA, grpc_core::ResourceQuota::Default()));
74 auto conn_handle = cf_engine->Connect(
75 [&client_signal](auto endpoint) {
76 EXPECT_EQ(endpoint.status().code(), absl::StatusCode::kCancelled);
77 client_signal.Notify();
78 },
79 *resolved_addr, config, memory_quota.CreateMemoryAllocator("conn1"), 1h);
80
81 cf_engine->CancelConnect(conn_handle);
82 client_signal.WaitForNotification();
83 }
84
85 namespace {
ResolvedAddressesToStrings(const std::vector<EventEngine::ResolvedAddress> addresses)86 std::vector<std::string> ResolvedAddressesToStrings(
87 const std::vector<EventEngine::ResolvedAddress> addresses) {
88 std::vector<std::string> ip_strings;
89 std::transform(addresses.cbegin(), addresses.cend(),
90 std::back_inserter(ip_strings), [](auto const& address) {
91 return ResolvedAddressToString(address).value_or("ERROR");
92 });
93 return ip_strings;
94 }
95 } // namespace
96
TEST(CFEventEngineTest,TestCreateDNSResolver)97 TEST(CFEventEngineTest, TestCreateDNSResolver) {
98 grpc_core::MemoryQuota memory_quota("cf_engine_test");
99 auto cf_engine = std::make_shared<CFEventEngine>();
100
101 EXPECT_TRUE(cf_engine->GetDNSResolver({}).status().ok());
102 EXPECT_TRUE(cf_engine->GetDNSResolver({.dns_server = ""}).status().ok());
103 EXPECT_EQ(
104 cf_engine->GetDNSResolver({.dns_server = "8.8.8.8"}).status().code(),
105 absl::StatusCode::kInvalidArgument);
106 EXPECT_EQ(
107 cf_engine->GetDNSResolver({.dns_server = "8.8.8.8:53"}).status().code(),
108 absl::StatusCode::kInvalidArgument);
109 EXPECT_EQ(
110 cf_engine->GetDNSResolver({.dns_server = "invalid"}).status().code(),
111 absl::StatusCode::kInvalidArgument);
112 }
113
TEST(CFEventEngineTest,TestResolveLocalhost)114 TEST(CFEventEngineTest, TestResolveLocalhost) {
115 grpc_core::Notification resolve_signal;
116
117 auto cf_engine = std::make_shared<CFEventEngine>();
118 auto dns_resolver = cf_engine->GetDNSResolver({});
119
120 dns_resolver.value()->LookupHostname(
121 [&resolve_signal](auto result) {
122 EXPECT_TRUE(result.status().ok());
123 EXPECT_THAT(ResolvedAddressesToStrings(result.value()),
124 testing::UnorderedElementsAre("127.0.0.1:80", "[::1]:80"));
125
126 resolve_signal.Notify();
127 },
128 "localhost", "80");
129
130 resolve_signal.WaitForNotification();
131 }
132
TEST(CFEventEngineTest,TestResolveRemote)133 TEST(CFEventEngineTest, TestResolveRemote) {
134 grpc_core::Notification resolve_signal;
135
136 auto cf_engine = std::make_shared<CFEventEngine>();
137 auto dns_resolver = cf_engine->GetDNSResolver({});
138
139 dns_resolver.value()->LookupHostname(
140 [&resolve_signal](auto result) {
141 EXPECT_TRUE(result.status().ok());
142 EXPECT_THAT(ResolvedAddressesToStrings(result.value()),
143 testing::UnorderedElementsAre("127.0.0.1:80", "[::1]:80"));
144
145 resolve_signal.Notify();
146 },
147 "localtest.me:80", "443");
148
149 resolve_signal.WaitForNotification();
150 }
151
TEST(CFEventEngineTest,TestResolveIPv4Remote)152 TEST(CFEventEngineTest, TestResolveIPv4Remote) {
153 grpc_core::Notification resolve_signal;
154
155 auto cf_engine = std::make_shared<CFEventEngine>();
156 auto dns_resolver = cf_engine->GetDNSResolver({});
157
158 dns_resolver.value()->LookupHostname(
159 [&resolve_signal](auto result) {
160 EXPECT_TRUE(result.status().ok());
161 EXPECT_THAT(ResolvedAddressesToStrings(result.value()),
162 testing::IsSubsetOf(
163 {"1.2.3.4:80", "[64:ff9b::102:304]:80" /*NAT64*/}));
164
165 resolve_signal.Notify();
166 },
167 "1.2.3.4.nip.io:80", "");
168
169 resolve_signal.WaitForNotification();
170 }
171
TEST(CFEventEngineTest,TestResolveIPv6Remote)172 TEST(CFEventEngineTest, TestResolveIPv6Remote) {
173 grpc_core::Notification resolve_signal;
174
175 auto cf_engine = std::make_shared<CFEventEngine>();
176 auto dns_resolver = cf_engine->GetDNSResolver({});
177
178 dns_resolver.value()->LookupHostname(
179 [&resolve_signal](auto result) {
180 EXPECT_TRUE(result.status().ok());
181 EXPECT_THAT(
182 ResolvedAddressesToStrings(result.value()),
183 testing::UnorderedElementsAre("[2607:f8b0:400a:801::1002]:80"));
184
185 resolve_signal.Notify();
186 },
187 "2607-f8b0-400a-801--1002.sslip.io.", "80");
188
189 resolve_signal.WaitForNotification();
190 }
191
TEST(CFEventEngineTest,TestResolveIPv4Literal)192 TEST(CFEventEngineTest, TestResolveIPv4Literal) {
193 grpc_core::Notification resolve_signal;
194
195 auto cf_engine = std::make_shared<CFEventEngine>();
196 auto dns_resolver = cf_engine->GetDNSResolver({});
197
198 dns_resolver.value()->LookupHostname(
199 [&resolve_signal](auto result) {
200 EXPECT_TRUE(result.status().ok());
201 EXPECT_THAT(ResolvedAddressesToStrings(result.value()),
202 testing::UnorderedElementsAre("1.2.3.4:443"));
203
204 resolve_signal.Notify();
205 },
206 "1.2.3.4", "https");
207
208 resolve_signal.WaitForNotification();
209 }
210
TEST(CFEventEngineTest,TestResolveIPv6Literal)211 TEST(CFEventEngineTest, TestResolveIPv6Literal) {
212 grpc_core::Notification resolve_signal;
213
214 auto cf_engine = std::make_shared<CFEventEngine>();
215 auto dns_resolver = cf_engine->GetDNSResolver({});
216
217 dns_resolver.value()->LookupHostname(
218 [&resolve_signal](auto result) {
219 EXPECT_TRUE(result.status().ok());
220 EXPECT_THAT(
221 ResolvedAddressesToStrings(result.value()),
222 testing::UnorderedElementsAre("[2607:f8b0:400a:801::1002]:443"));
223
224 resolve_signal.Notify();
225 },
226 "[2607:f8b0:400a:801::1002]", "443");
227
228 resolve_signal.WaitForNotification();
229 }
230
TEST(CFEventEngineTest,TestResolveNoRecord)231 TEST(CFEventEngineTest, TestResolveNoRecord) {
232 grpc_core::Notification resolve_signal;
233 auto cf_engine = std::make_shared<CFEventEngine>();
234 auto dns_resolver = std::move(cf_engine->GetDNSResolver({})).value();
235
236 dns_resolver->LookupHostname(
237 [&resolve_signal](auto result) {
238 EXPECT_EQ(result.status().code(), absl::StatusCode::kNotFound);
239
240 resolve_signal.Notify();
241 },
242 "nonexisting-target.dns-test.event-engine.", "443");
243
244 resolve_signal.WaitForNotification();
245 }
246
TEST(CFEventEngineTest,TestResolveCanceled)247 TEST(CFEventEngineTest, TestResolveCanceled) {
248 grpc_core::Notification resolve_signal;
249 auto cf_engine = std::make_shared<CFEventEngine>();
250 auto dns_resolver = std::move(cf_engine->GetDNSResolver({})).value();
251
252 dns_resolver->LookupHostname(
253 [&resolve_signal](auto result) {
254 // query may have already finished before canceling, only verity the
255 // code if status is not ok
256 if (!result.status().ok()) {
257 EXPECT_EQ(result.status().code(), absl::StatusCode::kCancelled);
258 }
259
260 resolve_signal.Notify();
261 },
262 "dont-care-since-wont-be-resolved.localtest.me", "443");
263
264 dns_resolver.reset();
265 resolve_signal.WaitForNotification();
266 }
267
TEST(CFEventEngineTest,TestResolveAgainInCallback)268 TEST(CFEventEngineTest, TestResolveAgainInCallback) {
269 std::atomic<int> times{2};
270 grpc_core::Notification resolve_signal;
271
272 auto cf_engine = std::make_shared<CFEventEngine>();
273 auto dns_resolver = std::move(cf_engine->GetDNSResolver({})).value();
274
275 dns_resolver->LookupHostname(
276 [&resolve_signal, ×, &dns_resolver](auto result) {
277 EXPECT_TRUE(result.status().ok());
278 EXPECT_THAT(ResolvedAddressesToStrings(result.value()),
279 testing::UnorderedElementsAre("127.0.0.1:80", "[::1]:80"));
280
281 dns_resolver->LookupHostname(
282 [&resolve_signal, ×](auto result) {
283 EXPECT_TRUE(result.status().ok());
284 EXPECT_THAT(
285 ResolvedAddressesToStrings(result.value()),
286 testing::UnorderedElementsAre("127.0.0.1:443", "[::1]:443"));
287
288 if (--times == 0) {
289 resolve_signal.Notify();
290 }
291 },
292 "localhost", "443");
293
294 if (--times == 0) {
295 resolve_signal.Notify();
296 }
297 },
298 "localhost", "80");
299
300 resolve_signal.WaitForNotification();
301 }
302
TEST(CFEventEngineTest,TestResolveMany)303 TEST(CFEventEngineTest, TestResolveMany) {
304 std::atomic<int> times{10};
305 grpc_core::Notification resolve_signal;
306 auto cf_engine = std::make_shared<CFEventEngine>();
307 auto dns_resolver = std::move(cf_engine->GetDNSResolver({})).value();
308
309 for (int i = times; i >= 1; --i) {
310 dns_resolver->LookupHostname(
311 [&resolve_signal, ×, i](auto result) {
312 EXPECT_TRUE(result.status().ok());
313 EXPECT_THAT(
314 ResolvedAddressesToStrings(result.value()),
315 testing::IsSubsetOf(
316 {absl::StrFormat("100.0.0.%d:443", i),
317 absl::StrFormat("[64:ff9b::6400:%x]:443", i) /*NAT64*/}));
318
319 if (--times == 0) {
320 resolve_signal.Notify();
321 }
322 },
323 absl::StrFormat("100.0.0.%d.nip.io", i), "443");
324 }
325
326 resolve_signal.WaitForNotification();
327 }
328
329 } // namespace experimental
330 } // namespace grpc_event_engine
331
main(int argc,char ** argv)332 int main(int argc, char** argv) {
333 ::testing::InitGoogleTest(&argc, argv);
334 grpc_init();
335 int status = RUN_ALL_TESTS();
336 grpc_shutdown();
337 return status;
338 }
339
340 #else // not GPR_APPLE
main(int,char **)341 int main(int /* argc */, char** /* argv */) { return 0; }
342 #endif
343