xref: /aosp_15_r20/external/grpc-grpc/test/core/event_engine/cf/cf_engine_test.cc (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
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, &times, &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, &times](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, &times, 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