OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/dns/dns_transaction.h" | 5 #include "net/dns/dns_transaction.h" |
6 | 6 |
7 #include <deque> | |
8 #include <vector> | |
9 | |
10 #include "base/bind.h" | 7 #include "base/bind.h" |
11 #include "base/test/test_timeouts.h" | 8 #include "base/memory/scoped_ptr.h" |
12 #include "base/time.h" | 9 #include "net/base/big_endian.h" |
13 #include "net/dns/dns_protocol.h" | 10 #include "net/base/net_log.h" |
14 #include "net/dns/dns_query.h" | 11 #include "net/base/sys_addrinfo.h" |
15 #include "net/dns/dns_response.h" | 12 #include "net/dns/dns_response.h" |
16 #include "net/dns/dns_session.h" | 13 #include "net/dns/dns_session.h" |
17 #include "net/dns/dns_test_util.h" | 14 #include "net/dns/dns_test_util.h" |
18 #include "net/socket/socket_test_util.h" | 15 #include "net/socket/socket_test_util.h" |
19 #include "testing/gtest/include/gtest/gtest.h" | 16 #include "testing/gtest/include/gtest/gtest.h" |
20 | 17 |
| 18 // TODO(szym): test timeout. |
| 19 // TODO(szym): test server fallback. |
| 20 // TODO(szym): test suffix search. |
| 21 |
21 namespace net { | 22 namespace net { |
22 | 23 |
23 namespace { | 24 namespace { |
24 | 25 |
25 // A mock for RandIntCallback that always returns 0. | |
26 int ReturnZero(int min, int max) { | |
27 return 0; | |
28 } | |
29 | |
30 class DnsTransactionTest : public testing::Test { | 26 class DnsTransactionTest : public testing::Test { |
| 27 public: |
| 28 class TestHelper { |
| 29 public: |
| 30 // If |answer_count| < 0, it is the expected error code. |
| 31 TestHelper(const char* name, |
| 32 uint16 type, |
| 33 const MockWrite& write, |
| 34 const MockRead& read, |
| 35 int answer_count) |
| 36 : qname(name), |
| 37 qtype(type), |
| 38 expected_answer_count(answer_count), |
| 39 completed(false) { |
| 40 writes.push_back(write); |
| 41 reads.push_back(read); |
| 42 ReadBigEndian<uint16>(write.data, &transaction_id); |
| 43 data.reset(new StaticSocketDataProvider(&reads[0], reads.size(), |
| 44 &writes[0], writes.size())); |
| 45 } |
| 46 |
| 47 void MakeRequest(DnsTransactionFactory* client) { |
| 48 EXPECT_EQ(NULL, transaction.get()); |
| 49 transaction = client->CreateTransaction( |
| 50 qname, |
| 51 qtype, |
| 52 base::Bind(&TestHelper::OnTransactionComplete, |
| 53 base::Unretained(this)), |
| 54 BoundNetLog()); |
| 55 EXPECT_EQ(qname, transaction->GetHostname()); |
| 56 EXPECT_EQ(qtype, transaction->GetType()); |
| 57 int rv = transaction->Start(); |
| 58 if (rv != ERR_IO_PENDING) { |
| 59 EXPECT_NE(OK, rv); |
| 60 EXPECT_EQ(expected_answer_count, rv); |
| 61 } |
| 62 } |
| 63 |
| 64 void Cancel() { |
| 65 ASSERT_TRUE(transaction.get() != NULL); |
| 66 transaction.reset(NULL); |
| 67 } |
| 68 |
| 69 void OnTransactionComplete(DnsTransaction* t, |
| 70 int rv, |
| 71 const DnsResponse* response) { |
| 72 EXPECT_FALSE(completed); |
| 73 EXPECT_EQ(transaction.get(), t); |
| 74 |
| 75 if (expected_answer_count >= 0) { |
| 76 EXPECT_EQ(OK, rv); |
| 77 EXPECT_EQ(expected_answer_count, response->answer_count()); |
| 78 |
| 79 DnsRecordParser parser = response->Parser(); |
| 80 DnsResourceRecord record; |
| 81 for (int i = 0; i < expected_answer_count; ++i) { |
| 82 EXPECT_TRUE(parser.ParseRecord(&record)); |
| 83 } |
| 84 EXPECT_TRUE(parser.AtEnd()); |
| 85 |
| 86 } else { |
| 87 EXPECT_EQ(expected_answer_count, rv); |
| 88 EXPECT_EQ(NULL, response); |
| 89 } |
| 90 |
| 91 completed = true; |
| 92 } |
| 93 |
| 94 void CancelOnTransactionComplete(DnsTransaction* req, |
| 95 int rv, |
| 96 const DnsResponse* response) { |
| 97 EXPECT_FALSE(completed); |
| 98 Cancel(); |
| 99 } |
| 100 |
| 101 std::string qname; |
| 102 uint16 qtype; |
| 103 std::vector<MockWrite> writes; |
| 104 std::vector<MockRead> reads; |
| 105 uint16 transaction_id; // Id from first write. |
| 106 scoped_ptr<StaticSocketDataProvider> data; |
| 107 scoped_ptr<DnsTransaction> transaction; |
| 108 int expected_answer_count; |
| 109 |
| 110 bool completed; |
| 111 }; |
| 112 |
| 113 virtual void SetUp() OVERRIDE { |
| 114 helpers_.push_back(new TestHelper( |
| 115 kT0HostName, |
| 116 kT0Qtype, |
| 117 MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram), |
| 118 arraysize(kT0QueryDatagram)), |
| 119 MockRead(true, reinterpret_cast<const char*>(kT0ResponseDatagram), |
| 120 arraysize(kT0ResponseDatagram)), |
| 121 arraysize(kT0IpAddresses) + 1)); // +1 for CNAME RR |
| 122 |
| 123 helpers_.push_back(new TestHelper( |
| 124 kT1HostName, |
| 125 kT1Qtype, |
| 126 MockWrite(true, reinterpret_cast<const char*>(kT1QueryDatagram), |
| 127 arraysize(kT1QueryDatagram)), |
| 128 MockRead(true, reinterpret_cast<const char*>(kT1ResponseDatagram), |
| 129 arraysize(kT1ResponseDatagram)), |
| 130 arraysize(kT1IpAddresses) + 1)); // +1 for CNAME RR |
| 131 |
| 132 helpers_.push_back(new TestHelper( |
| 133 kT2HostName, |
| 134 kT2Qtype, |
| 135 MockWrite(true, reinterpret_cast<const char*>(kT2QueryDatagram), |
| 136 arraysize(kT2QueryDatagram)), |
| 137 MockRead(true, reinterpret_cast<const char*>(kT2ResponseDatagram), |
| 138 arraysize(kT2ResponseDatagram)), |
| 139 arraysize(kT2IpAddresses) + 1)); // +1 for CNAME RR |
| 140 |
| 141 helpers_.push_back(new TestHelper( |
| 142 kT3HostName, |
| 143 kT3Qtype, |
| 144 MockWrite(true, reinterpret_cast<const char*>(kT3QueryDatagram), |
| 145 arraysize(kT3QueryDatagram)), |
| 146 MockRead(true, reinterpret_cast<const char*>(kT3ResponseDatagram), |
| 147 arraysize(kT3ResponseDatagram)), |
| 148 arraysize(kT3IpAddresses) + 2)); // +2 for CNAME RR |
| 149 |
| 150 CreateFactory(); |
| 151 } |
| 152 |
| 153 void CreateFactory() { |
| 154 MockClientSocketFactory* socket_factory = new MockClientSocketFactory(); |
| 155 |
| 156 transaction_ids_.clear(); |
| 157 for (unsigned i = 0; i < helpers_.size(); ++i) { |
| 158 socket_factory->AddSocketDataProvider(helpers_[i]->data.get()); |
| 159 transaction_ids_.push_back(static_cast<int>(helpers_[i]->transaction_id)); |
| 160 } |
| 161 |
| 162 DnsConfig config; |
| 163 |
| 164 IPEndPoint dns_server; |
| 165 { |
| 166 bool rv = CreateDnsAddress(kDnsIp, kDnsPort, &dns_server); |
| 167 EXPECT_TRUE(rv); |
| 168 } |
| 169 config.nameservers.push_back(dns_server); |
| 170 |
| 171 DnsSession* session = new DnsSession( |
| 172 config, |
| 173 socket_factory, |
| 174 base::Bind(&DnsTransactionTest::GetNextId, base::Unretained(this)), |
| 175 NULL /* NetLog */); |
| 176 |
| 177 factory_ = DnsTransactionFactory::CreateFactory(session); |
| 178 } |
| 179 |
| 180 virtual void TearDown() OVERRIDE { |
| 181 STLDeleteElements(&helpers_); |
| 182 } |
| 183 |
| 184 int GetNextId(int min, int max) { |
| 185 EXPECT_FALSE(transaction_ids_.empty()); |
| 186 int id = transaction_ids_.front(); |
| 187 transaction_ids_.pop_front(); |
| 188 EXPECT_GE(id, min); |
| 189 EXPECT_LE(id, max); |
| 190 return id; |
| 191 } |
| 192 |
31 protected: | 193 protected: |
32 virtual void SetUp() OVERRIDE { | 194 std::vector<TestHelper*> helpers_; |
33 callback_ = base::Bind(&DnsTransactionTest::OnTransactionComplete, | 195 std::deque<int> transaction_ids_; |
34 base::Unretained(this)); | 196 scoped_ptr<DnsTransactionFactory> factory_; |
35 qname_ = std::string(kT0DnsName, arraysize(kT0DnsName)); | |
36 // Use long timeout to prevent timing out on slow bots. | |
37 ConfigureSession(base::TimeDelta::FromMilliseconds( | |
38 TestTimeouts::action_timeout_ms())); | |
39 } | |
40 | |
41 void ConfigureSession(const base::TimeDelta& timeout) { | |
42 IPEndPoint dns_server; | |
43 bool rv = CreateDnsAddress(kDnsIp, kDnsPort, &dns_server); | |
44 ASSERT_TRUE(rv); | |
45 | |
46 DnsConfig config; | |
47 config.nameservers.push_back(dns_server); | |
48 config.attempts = 3; | |
49 config.timeout = timeout; | |
50 | |
51 session_ = new DnsSession(config, | |
52 new MockClientSocketFactory(), | |
53 base::Bind(&ReturnZero), | |
54 NULL /* NetLog */); | |
55 } | |
56 | |
57 void StartTransaction() { | |
58 transaction_.reset(new DnsTransaction(session_.get(), | |
59 qname_, | |
60 kT0Qtype, | |
61 callback_, | |
62 BoundNetLog())); | |
63 | |
64 int rv0 = transaction_->Start(); | |
65 EXPECT_EQ(ERR_IO_PENDING, rv0); | |
66 } | |
67 | |
68 void OnTransactionComplete(DnsTransaction* transaction, int rv) { | |
69 EXPECT_EQ(transaction_.get(), transaction); | |
70 EXPECT_EQ(qname_, transaction->query()->qname().as_string()); | |
71 EXPECT_EQ(kT0Qtype, transaction->query()->qtype()); | |
72 rv_ = rv; | |
73 MessageLoop::current()->Quit(); | |
74 } | |
75 | |
76 MockClientSocketFactory& factory() { | |
77 return *static_cast<MockClientSocketFactory*>(session_->socket_factory()); | |
78 } | |
79 | |
80 int rv() const { return rv_; } | |
81 | |
82 private: | |
83 DnsTransaction::ResultCallback callback_; | |
84 std::string qname_; | |
85 scoped_refptr<DnsSession> session_; | |
86 scoped_ptr<DnsTransaction> transaction_; | |
87 | |
88 int rv_; | |
89 }; | 197 }; |
90 | 198 |
91 TEST_F(DnsTransactionTest, NormalQueryResponseTest) { | 199 TEST_F(DnsTransactionTest, Lookup) { |
92 MockWrite writes0[] = { | 200 helpers_[0]->MakeRequest(factory_.get()); |
93 MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram), | 201 |
94 arraysize(kT0QueryDatagram)) | 202 // Wait until result. |
95 }; | 203 MessageLoop::current()->RunAllPending(); |
96 | 204 |
97 MockRead reads0[] = { | 205 EXPECT_TRUE(helpers_[0]->completed); |
98 MockRead(true, reinterpret_cast<const char*>(kT0ResponseDatagram), | 206 } |
99 arraysize(kT0ResponseDatagram)) | 207 |
100 }; | 208 TEST_F(DnsTransactionTest, ConcurrentLookup) { |
101 | 209 for (unsigned i = 0; i < helpers_.size(); ++i) { |
102 StaticSocketDataProvider data(reads0, arraysize(reads0), | 210 helpers_[i]->MakeRequest(factory_.get()); |
103 writes0, arraysize(writes0)); | 211 } |
104 factory().AddSocketDataProvider(&data); | 212 |
105 | 213 MessageLoop::current()->RunAllPending(); |
106 StartTransaction(); | 214 |
107 MessageLoop::current()->Run(); | 215 for (unsigned i = 0; i < helpers_.size(); ++i) { |
108 | 216 EXPECT_TRUE(helpers_[i]->completed); |
109 EXPECT_EQ(OK, rv()); | 217 } |
110 // TODO(szym): test fields of |transaction_->response()| | 218 } |
111 | 219 |
112 EXPECT_TRUE(data.at_read_eof()); | 220 TEST_F(DnsTransactionTest, CancelLookup) { |
113 EXPECT_TRUE(data.at_write_eof()); | 221 for (unsigned i = 0; i < helpers_.size(); ++i) { |
114 } | 222 helpers_[i]->MakeRequest(factory_.get()); |
115 | 223 } |
116 TEST_F(DnsTransactionTest, MismatchedQueryResponseTest) { | 224 |
117 MockWrite writes0[] = { | 225 helpers_[0]->Cancel(); |
118 MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram), | 226 helpers_[2]->Cancel(); |
119 arraysize(kT0QueryDatagram)) | 227 |
120 }; | 228 MessageLoop::current()->RunAllPending(); |
121 | 229 |
122 MockRead reads1[] = { | 230 EXPECT_FALSE(helpers_[0]->completed); |
123 MockRead(true, reinterpret_cast<const char*>(kT1ResponseDatagram), | 231 EXPECT_TRUE(helpers_[1]->completed); |
124 arraysize(kT1ResponseDatagram)) | 232 EXPECT_FALSE(helpers_[2]->completed); |
125 }; | 233 EXPECT_TRUE(helpers_[3]->completed); |
126 | 234 } |
127 StaticSocketDataProvider data(reads1, arraysize(reads1), | 235 |
128 writes0, arraysize(writes0)); | 236 TEST_F(DnsTransactionTest, DestroyClient) { |
129 factory().AddSocketDataProvider(&data); | 237 for (unsigned i = 0; i < helpers_.size(); ++i) { |
130 | 238 helpers_[i]->MakeRequest(factory_.get()); |
131 StartTransaction(); | 239 } |
132 MessageLoop::current()->Run(); | 240 |
133 | 241 // Destroying the client does not affect running requests. |
134 EXPECT_EQ(ERR_DNS_MALFORMED_RESPONSE, rv()); | 242 factory_.reset(NULL); |
135 | 243 |
136 EXPECT_TRUE(data.at_read_eof()); | 244 MessageLoop::current()->RunAllPending(); |
137 EXPECT_TRUE(data.at_write_eof()); | 245 |
138 } | 246 for (unsigned i = 0; i < helpers_.size(); ++i) { |
139 | 247 EXPECT_TRUE(helpers_[i]->completed); |
140 // Test that after the first timeout we do a fresh connection and if we get | 248 } |
141 // a response on the new connection, we return it. | 249 } |
142 TEST_F(DnsTransactionTest, FirstTimeoutTest) { | 250 |
143 MockWrite writes0[] = { | 251 TEST_F(DnsTransactionTest, DestroyRequestFromCallback) { |
144 MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram), | 252 // Custom callback to cancel the completing request. |
145 arraysize(kT0QueryDatagram)) | 253 helpers_[0]->transaction = factory_->CreateTransaction( |
146 }; | 254 helpers_[0]->qname, |
147 | 255 helpers_[0]->qtype, |
148 MockRead reads0[] = { | 256 base::Bind(&TestHelper::CancelOnTransactionComplete, |
149 MockRead(true, reinterpret_cast<const char*>(kT0ResponseDatagram), | 257 base::Unretained(helpers_[0])), |
150 arraysize(kT0ResponseDatagram)) | 258 BoundNetLog()); |
151 }; | 259 |
152 | 260 int rv = helpers_[0]->transaction->Start(); |
153 scoped_refptr<DelayedSocketData> socket0_data( | 261 EXPECT_EQ(ERR_IO_PENDING, rv); |
154 new DelayedSocketData(2, NULL, 0, writes0, arraysize(writes0))); | 262 |
155 scoped_refptr<DelayedSocketData> socket1_data( | 263 for (unsigned i = 1; i < helpers_.size(); ++i) { |
156 new DelayedSocketData(0, reads0, arraysize(reads0), | 264 helpers_[i]->MakeRequest(factory_.get()); |
157 writes0, arraysize(writes0))); | 265 } |
158 | 266 |
159 // Use short timeout to speed up the test. | 267 MessageLoop::current()->RunAllPending(); |
160 ConfigureSession(base::TimeDelta::FromMilliseconds( | 268 |
161 TestTimeouts::tiny_timeout_ms())); | 269 EXPECT_FALSE(helpers_[0]->completed); |
162 factory().AddSocketDataProvider(socket0_data.get()); | 270 for (unsigned i = 1; i < helpers_.size(); ++i) { |
163 factory().AddSocketDataProvider(socket1_data.get()); | 271 EXPECT_TRUE(helpers_[i]->completed); |
164 | 272 } |
165 StartTransaction(); | 273 } |
166 | 274 |
167 MessageLoop::current()->Run(); | 275 TEST_F(DnsTransactionTest, HandleFailure) { |
168 | 276 STLDeleteElements(&helpers_); |
169 EXPECT_EQ(OK, rv()); | 277 // Wrong question. |
170 | 278 helpers_.push_back(new TestHelper( |
171 EXPECT_TRUE(socket0_data->at_read_eof()); | 279 kT0HostName, |
172 EXPECT_TRUE(socket0_data->at_write_eof()); | 280 kT0Qtype, |
173 EXPECT_TRUE(socket1_data->at_read_eof()); | 281 MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram), |
174 EXPECT_TRUE(socket1_data->at_write_eof()); | 282 arraysize(kT0QueryDatagram)), |
175 EXPECT_EQ(2u, factory().udp_client_sockets().size()); | 283 MockRead(true, reinterpret_cast<const char*>(kT1ResponseDatagram), |
176 } | 284 arraysize(kT1ResponseDatagram)), |
177 | 285 ERR_DNS_MALFORMED_RESPONSE)); |
178 // Test that after the first timeout we do a fresh connection, and after | 286 |
179 // the second timeout we do another fresh connection, and if we get a | 287 // Response with NXDOMAIN. |
180 // response on the second connection, we return it. | 288 uint8 nxdomain_response[arraysize(kT0QueryDatagram)]; |
181 TEST_F(DnsTransactionTest, SecondTimeoutTest) { | 289 memcpy(nxdomain_response, kT0QueryDatagram, arraysize(nxdomain_response)); |
182 MockWrite writes0[] = { | 290 nxdomain_response[2] &= 0x80; // Response bit. |
183 MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram), | 291 nxdomain_response[3] &= 0x03; // NXDOMAIN bit. |
184 arraysize(kT0QueryDatagram)) | 292 helpers_.push_back(new TestHelper( |
185 }; | 293 kT0HostName, |
186 | 294 kT0Qtype, |
187 MockRead reads0[] = { | 295 MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram), |
188 MockRead(true, reinterpret_cast<const char*>(kT0ResponseDatagram), | 296 arraysize(kT0QueryDatagram)), |
189 arraysize(kT0ResponseDatagram)) | 297 MockRead(true, reinterpret_cast<const char*>(nxdomain_response), |
190 }; | 298 arraysize(nxdomain_response)), |
191 | 299 ERR_NAME_NOT_RESOLVED)); |
192 scoped_refptr<DelayedSocketData> socket0_data( | 300 |
193 new DelayedSocketData(2, NULL, 0, writes0, arraysize(writes0))); | 301 CreateFactory(); |
194 scoped_refptr<DelayedSocketData> socket1_data( | 302 |
195 new DelayedSocketData(2, NULL, 0, writes0, arraysize(writes0))); | 303 for (unsigned i = 0; i < helpers_.size(); ++i) { |
196 scoped_refptr<DelayedSocketData> socket2_data( | 304 helpers_[i]->MakeRequest(factory_.get()); |
197 new DelayedSocketData(0, reads0, arraysize(reads0), | 305 } |
198 writes0, arraysize(writes0))); | 306 |
199 | 307 MessageLoop::current()->RunAllPending(); |
200 // Use short timeout to speed up the test. | 308 |
201 ConfigureSession(base::TimeDelta::FromMilliseconds( | 309 for (unsigned i = 0; i < helpers_.size(); ++i) { |
202 TestTimeouts::tiny_timeout_ms())); | 310 EXPECT_TRUE(helpers_[i]->completed); |
203 factory().AddSocketDataProvider(socket0_data.get()); | 311 } |
204 factory().AddSocketDataProvider(socket1_data.get()); | |
205 factory().AddSocketDataProvider(socket2_data.get()); | |
206 | |
207 StartTransaction(); | |
208 | |
209 MessageLoop::current()->Run(); | |
210 | |
211 EXPECT_EQ(OK, rv()); | |
212 | |
213 EXPECT_TRUE(socket0_data->at_read_eof()); | |
214 EXPECT_TRUE(socket0_data->at_write_eof()); | |
215 EXPECT_TRUE(socket1_data->at_read_eof()); | |
216 EXPECT_TRUE(socket1_data->at_write_eof()); | |
217 EXPECT_TRUE(socket2_data->at_read_eof()); | |
218 EXPECT_TRUE(socket2_data->at_write_eof()); | |
219 EXPECT_EQ(3u, factory().udp_client_sockets().size()); | |
220 } | |
221 | |
222 // Test that after the first timeout we do a fresh connection, and after | |
223 // the second timeout we do another fresh connection and after the third | |
224 // timeout we give up and return a timeout error. | |
225 TEST_F(DnsTransactionTest, ThirdTimeoutTest) { | |
226 MockWrite writes0[] = { | |
227 MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram), | |
228 arraysize(kT0QueryDatagram)) | |
229 }; | |
230 | |
231 scoped_refptr<DelayedSocketData> socket0_data( | |
232 new DelayedSocketData(2, NULL, 0, writes0, arraysize(writes0))); | |
233 scoped_refptr<DelayedSocketData> socket1_data( | |
234 new DelayedSocketData(2, NULL, 0, writes0, arraysize(writes0))); | |
235 scoped_refptr<DelayedSocketData> socket2_data( | |
236 new DelayedSocketData(2, NULL, 0, writes0, arraysize(writes0))); | |
237 | |
238 // Use short timeout to speed up the test. | |
239 ConfigureSession(base::TimeDelta::FromMilliseconds( | |
240 TestTimeouts::tiny_timeout_ms())); | |
241 factory().AddSocketDataProvider(socket0_data.get()); | |
242 factory().AddSocketDataProvider(socket1_data.get()); | |
243 factory().AddSocketDataProvider(socket2_data.get()); | |
244 | |
245 StartTransaction(); | |
246 | |
247 MessageLoop::current()->Run(); | |
248 | |
249 EXPECT_EQ(ERR_DNS_TIMED_OUT, rv()); | |
250 | |
251 EXPECT_TRUE(socket0_data->at_read_eof()); | |
252 EXPECT_TRUE(socket0_data->at_write_eof()); | |
253 EXPECT_TRUE(socket1_data->at_read_eof()); | |
254 EXPECT_TRUE(socket1_data->at_write_eof()); | |
255 EXPECT_TRUE(socket2_data->at_read_eof()); | |
256 EXPECT_TRUE(socket2_data->at_write_eof()); | |
257 EXPECT_EQ(3u, factory().udp_client_sockets().size()); | |
258 } | 312 } |
259 | 313 |
260 } // namespace | 314 } // namespace |
261 | 315 |
262 } // namespace net | 316 } // namespace net |
| 317 |
OLD | NEW |