1 // Copyright (c) 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // This unit test relies on /proc, which is not available on non-Linux based
6 // OSes that we support.
7 #if defined(__linux__)
8
9 #include "quiche/quic/tools/quic_default_client.h"
10
11 #include <dirent.h>
12 #include <sys/types.h>
13
14 #include <memory>
15 #include <utility>
16
17 #include "absl/strings/match.h"
18 #include "absl/strings/string_view.h"
19 #include "quiche/quic/core/io/quic_default_event_loop.h"
20 #include "quiche/quic/core/io/quic_event_loop.h"
21 #include "quiche/quic/core/quic_default_clock.h"
22 #include "quiche/quic/platform/api/quic_test.h"
23 #include "quiche/quic/platform/api/quic_test_loopback.h"
24 #include "quiche/quic/test_tools/crypto_test_utils.h"
25 #include "quiche/common/quiche_text_utils.h"
26
27 namespace quic {
28 namespace test {
29 namespace {
30
31 const char* kPathToFds = "/proc/self/fd";
32
33 // Return the value of a symbolic link in |path|, if |path| is not found, return
34 // an empty string.
ReadLink(const std::string & path)35 std::string ReadLink(const std::string& path) {
36 std::string result(PATH_MAX, '\0');
37 ssize_t result_size = readlink(path.c_str(), &result[0], result.size());
38 if (result_size < 0 && errno == ENOENT) {
39 return "";
40 }
41 QUICHE_CHECK(result_size > 0 &&
42 static_cast<size_t>(result_size) < result.size())
43 << "result_size:" << result_size << ", errno:" << errno
44 << ", path:" << path;
45 result.resize(result_size);
46 return result;
47 }
48
49 // Counts the number of open sockets for the current process.
NumOpenSocketFDs()50 size_t NumOpenSocketFDs() {
51 size_t socket_count = 0;
52 dirent* file;
53 std::unique_ptr<DIR, int (*)(DIR*)> fd_directory(opendir(kPathToFds),
54 closedir);
55 while ((file = readdir(fd_directory.get())) != nullptr) {
56 absl::string_view name(file->d_name);
57 if (name == "." || name == "..") {
58 continue;
59 }
60
61 std::string fd_path = ReadLink(absl::StrCat(kPathToFds, "/", name));
62 if (absl::StartsWith(fd_path, "socket:")) {
63 socket_count++;
64 }
65 }
66 return socket_count;
67 }
68
69 class QuicDefaultClientTest : public QuicTest {
70 public:
QuicDefaultClientTest()71 QuicDefaultClientTest()
72 : event_loop_(GetDefaultEventLoop()->Create(QuicDefaultClock::Get())) {
73 // Creates and destroys a single client first which may open persistent
74 // sockets when initializing platform dependencies like certificate
75 // verifier. Future creation of addtional clients will deterministically
76 // open one socket per client.
77 CreateAndInitializeQuicClient();
78 }
79
80 // Creates a new QuicClient and Initializes it on an unused port.
81 // Caller is responsible for deletion.
CreateAndInitializeQuicClient()82 std::unique_ptr<QuicDefaultClient> CreateAndInitializeQuicClient() {
83 QuicSocketAddress server_address(QuicSocketAddress(TestLoopback(), 0));
84 QuicServerId server_id("hostname", server_address.port(), false);
85 ParsedQuicVersionVector versions = AllSupportedVersions();
86 auto client = std::make_unique<QuicDefaultClient>(
87 server_address, server_id, versions, event_loop_.get(),
88 crypto_test_utils::ProofVerifierForTesting());
89 EXPECT_TRUE(client->Initialize());
90 return client;
91 }
92
93 private:
94 std::unique_ptr<QuicEventLoop> event_loop_;
95 };
96
TEST_F(QuicDefaultClientTest,DoNotLeakSocketFDs)97 TEST_F(QuicDefaultClientTest, DoNotLeakSocketFDs) {
98 // Make sure that the QuicClient doesn't leak socket FDs. Doing so could cause
99 // port exhaustion in long running processes which repeatedly create clients.
100
101 // Record the initial number of FDs.
102 size_t number_of_open_fds = NumOpenSocketFDs();
103
104 // Create a number of clients, initialize them, and verify this has resulted
105 // in additional FDs being opened.
106 const int kNumClients = 50;
107 for (int i = 0; i < kNumClients; ++i) {
108 EXPECT_EQ(number_of_open_fds, NumOpenSocketFDs());
109 std::unique_ptr<QuicDefaultClient> client(CreateAndInitializeQuicClient());
110 // Initializing the client will create a new FD.
111 EXPECT_EQ(number_of_open_fds + 1, NumOpenSocketFDs());
112 }
113
114 // The FDs created by the QuicClients should now be closed.
115 EXPECT_EQ(number_of_open_fds, NumOpenSocketFDs());
116 }
117
TEST_F(QuicDefaultClientTest,CreateAndCleanUpUDPSockets)118 TEST_F(QuicDefaultClientTest, CreateAndCleanUpUDPSockets) {
119 size_t number_of_open_fds = NumOpenSocketFDs();
120
121 std::unique_ptr<QuicDefaultClient> client(CreateAndInitializeQuicClient());
122 // Creating and initializing a client will result in one socket being opened.
123 EXPECT_EQ(number_of_open_fds + 1, NumOpenSocketFDs());
124
125 // Create more UDP sockets.
126 EXPECT_TRUE(client->default_network_helper()->CreateUDPSocketAndBind(
127 client->server_address(), client->bind_to_address(),
128 client->local_port()));
129 EXPECT_EQ(number_of_open_fds + 2, NumOpenSocketFDs());
130 EXPECT_TRUE(client->default_network_helper()->CreateUDPSocketAndBind(
131 client->server_address(), client->bind_to_address(),
132 client->local_port()));
133 EXPECT_EQ(number_of_open_fds + 3, NumOpenSocketFDs());
134
135 // Clean up UDP sockets.
136 client->default_network_helper()->CleanUpUDPSocket(client->GetLatestFD());
137 EXPECT_EQ(number_of_open_fds + 2, NumOpenSocketFDs());
138 client->default_network_helper()->CleanUpUDPSocket(client->GetLatestFD());
139 EXPECT_EQ(number_of_open_fds + 1, NumOpenSocketFDs());
140 }
141
142 } // namespace
143 } // namespace test
144 } // namespace quic
145
146 #endif // defined(__linux__)
147