OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/http/http_server_properties_impl.h" | 5 #include "net/http/http_server_properties_impl.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/memory/scoped_ptr.h" | 9 #include "base/memory/scoped_ptr.h" |
10 #include "base/message_loop/message_loop.h" | 10 #include "base/message_loop/message_loop.h" |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
43 return; | 43 return; |
44 // Add the entries from persisted data. | 44 // Add the entries from persisted data. |
45 for (std::vector<std::string>::reverse_iterator it = spdy_servers->rbegin(); | 45 for (std::vector<std::string>::reverse_iterator it = spdy_servers->rbegin(); |
46 it != spdy_servers->rend(); ++it) { | 46 it != spdy_servers->rend(); ++it) { |
47 spdy_servers_map_.Put(*it, support_spdy); | 47 spdy_servers_map_.Put(*it, support_spdy); |
48 } | 48 } |
49 } | 49 } |
50 | 50 |
51 void HttpServerPropertiesImpl::InitializeAlternateProtocolServers( | 51 void HttpServerPropertiesImpl::InitializeAlternateProtocolServers( |
52 AlternateProtocolMap* alternate_protocol_map) { | 52 AlternateProtocolMap* alternate_protocol_map) { |
53 // Keep all the broken ones since those don't get persisted. | 53 for (AlternateProtocolMap::iterator alternate_protocols = |
54 for (AlternateProtocolMap::iterator it = alternate_protocol_map_.begin(); | 54 alternate_protocol_map_.begin(); |
55 it != alternate_protocol_map_.end();) { | 55 alternate_protocols != alternate_protocol_map_.end();) { |
56 AlternateProtocolMap::iterator old_it = it; | 56 // TODO(bnc): Factor out code shared with ClearNonBrokenAlternateProtocols |
57 ++it; | 57 // into separate method. |
58 if (!old_it->second.is_broken) { | 58 // Keep all the broken ones since those don't get persisted. |
59 alternate_protocol_map_.Erase(old_it); | 59 for (AlternateProtocols::iterator it = alternate_protocols->second.begin(); |
| 60 it != alternate_protocols->second.end();) { |
| 61 if (!it->is_broken) { |
| 62 it = alternate_protocols->second.erase(it); |
| 63 } else { |
| 64 ++it; |
| 65 } |
| 66 } |
| 67 if (alternate_protocols->second.size() == 0) { |
| 68 RemoveCanonicalHost(alternate_protocols->first); |
| 69 alternate_protocols = alternate_protocol_map_.Erase(alternate_protocols); |
| 70 } else { |
| 71 ++alternate_protocols; |
60 } | 72 } |
61 } | 73 } |
62 | 74 |
63 // Add the entries from persisted data. | 75 // Add the entries from persisted data. |
64 for (AlternateProtocolMap::reverse_iterator it = | 76 for (AlternateProtocolMap::reverse_iterator it = |
65 alternate_protocol_map->rbegin(); | 77 alternate_protocol_map->rbegin(); |
66 it != alternate_protocol_map->rend(); ++it) { | 78 it != alternate_protocol_map->rend(); ++it) { |
67 alternate_protocol_map_.Put(it->first, it->second); | 79 alternate_protocol_map_.Put(it->first, it->second); |
68 } | 80 } |
69 | 81 |
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
218 | 230 |
219 void HttpServerPropertiesImpl::MaybeForceHTTP11(const HostPortPair& server, | 231 void HttpServerPropertiesImpl::MaybeForceHTTP11(const HostPortPair& server, |
220 SSLConfig* ssl_config) { | 232 SSLConfig* ssl_config) { |
221 if (RequiresHTTP11(server)) { | 233 if (RequiresHTTP11(server)) { |
222 ForceHTTP11(ssl_config); | 234 ForceHTTP11(ssl_config); |
223 } | 235 } |
224 } | 236 } |
225 | 237 |
226 bool HttpServerPropertiesImpl::HasAlternateProtocol( | 238 bool HttpServerPropertiesImpl::HasAlternateProtocol( |
227 const HostPortPair& server) { | 239 const HostPortPair& server) { |
228 if (g_forced_alternate_protocol) | 240 return GetAlternateProtocol(server).protocol != |
229 return true; | 241 UNINITIALIZED_ALTERNATE_PROTOCOL; |
230 AlternateProtocolMap::const_iterator it = | |
231 GetAlternateProtocolIterator(server); | |
232 return it != alternate_protocol_map_.end() && | |
233 it->second.probability >= alternate_protocol_probability_threshold_; | |
234 } | 242 } |
235 | 243 |
236 std::string HttpServerPropertiesImpl::GetCanonicalSuffix( | 244 std::string HttpServerPropertiesImpl::GetCanonicalSuffix( |
237 const HostPortPair& server) { | 245 const HostPortPair& server) { |
238 // If this host ends with a canonical suffix, then return the canonical | 246 // If this host ends with a canonical suffix, then return the canonical |
239 // suffix. | 247 // suffix. |
240 for (size_t i = 0; i < canonical_suffixes_.size(); ++i) { | 248 for (size_t i = 0; i < canonical_suffixes_.size(); ++i) { |
241 std::string canonical_suffix = canonical_suffixes_[i]; | 249 std::string canonical_suffix = canonical_suffixes_[i]; |
242 if (EndsWith(server.host(), canonical_suffixes_[i], false)) { | 250 if (EndsWith(server.host(), canonical_suffixes_[i], false)) { |
243 return canonical_suffix; | 251 return canonical_suffix; |
244 } | 252 } |
245 } | 253 } |
246 return std::string(); | 254 return std::string(); |
247 } | 255 } |
248 | 256 |
249 AlternateProtocolInfo | 257 AlternateProtocolInfo HttpServerPropertiesImpl::GetAlternateProtocol( |
250 HttpServerPropertiesImpl::GetAlternateProtocol( | |
251 const HostPortPair& server) { | 258 const HostPortPair& server) { |
252 DCHECK(HasAlternateProtocol(server)); | 259 AlternateProtocolMap::const_iterator alternate_protocols = |
| 260 GetAlternateProtocolIterator(server); |
| 261 if (alternate_protocols != alternate_protocol_map_.end()) { |
| 262 for (const AlternateProtocolInfo& alternate_protocol : |
| 263 alternate_protocols->second) { |
| 264 if (!alternate_protocol.is_broken && |
| 265 alternate_protocol.probability >= |
| 266 alternate_protocol_probability_threshold_) { |
| 267 return alternate_protocol; |
| 268 } |
| 269 } |
| 270 } |
253 | 271 |
254 AlternateProtocolMap::const_iterator it = | 272 if (g_forced_alternate_protocol) |
255 GetAlternateProtocolIterator(server); | 273 return *g_forced_alternate_protocol; |
256 if (it != alternate_protocol_map_.end()) | |
257 return it->second; | |
258 | 274 |
259 // We must be forcing an alternate. | 275 AlternateProtocolInfo uninitialized_alternate_protocol; |
260 DCHECK(g_forced_alternate_protocol); | 276 return uninitialized_alternate_protocol; |
261 return *g_forced_alternate_protocol; | |
262 } | 277 } |
263 | 278 |
264 void HttpServerPropertiesImpl::SetAlternateProtocol( | 279 void HttpServerPropertiesImpl::AddAlternateProtocol( |
265 const HostPortPair& server, | 280 const HostPortPair& server, |
266 uint16 alternate_port, | 281 uint16 alternate_port, |
267 AlternateProtocol alternate_protocol, | 282 AlternateProtocol alternate_protocol, |
268 double alternate_probability) { | 283 double alternate_probability) { |
269 | 284 if (!IsAlternateProtocolValid(alternate_protocol)) |
| 285 return; |
270 AlternateProtocolInfo alternate(alternate_port, | 286 AlternateProtocolInfo alternate(alternate_port, |
271 alternate_protocol, | 287 alternate_protocol, |
272 alternate_probability); | 288 alternate_probability); |
273 AlternateProtocolMap::const_iterator it = | 289 AlternateProtocolMap::const_iterator map_it = |
274 GetAlternateProtocolIterator(server); | 290 alternate_protocol_map_.Get(server); |
275 if (it != alternate_protocol_map_.end()) { | 291 if (map_it != alternate_protocol_map_.end()) { |
276 const AlternateProtocolInfo existing_alternate = it->second; | 292 AlternateProtocols existing_alternates = map_it->second; |
277 | 293 AlternateProtocols::iterator it; |
278 if (existing_alternate.is_broken) { | 294 for (it = existing_alternates.begin(); it != existing_alternates.end(); |
279 DVLOG(1) << "Ignore alternate protocol since it's known to be broken."; | 295 ++it) { |
280 return; | 296 if (it->EqualsModuloProbability(alternate)) { |
| 297 break; |
| 298 } |
281 } | 299 } |
282 | 300 |
283 if (!existing_alternate.Equals(alternate)) { | 301 if (it != existing_alternates.end()) { |
284 LOG(WARNING) << "Changing the alternate protocol for: " | 302 if (it->is_broken) { |
285 << server.ToString() | 303 DVLOG(1) << "Ignore alternate protocol since it's known to be broken."; |
286 << " from [Port: " << existing_alternate.port | 304 return; |
287 << ", Protocol: " << existing_alternate.protocol | 305 } |
288 << ", Probability: " << existing_alternate.probability | 306 if (it->probability != alternate_probability) { |
289 << "] to [Port: " << alternate_port | 307 LOG(WARNING) << "Changing the probability of alternate protocol for: " |
290 << ", Protocol: " << alternate_protocol | 308 << server.ToString() << " Port: " << it->port |
291 << ", Probability: " << alternate_probability | 309 << ", Protocol: " << it->protocol |
292 << "]."; | 310 << ", Probability from: " << it->probability |
| 311 << " to: " << alternate_probability << "."; |
| 312 it->probability = alternate_probability; |
| 313 } |
| 314 } else { |
| 315 existing_alternates.push_back(alternate); |
293 } | 316 } |
| 317 |
| 318 alternate_protocol_map_.Put(server, existing_alternates); |
294 } else { | 319 } else { |
295 if (alternate_probability >= alternate_protocol_probability_threshold_) { | 320 if (alternate_probability >= alternate_protocol_probability_threshold_) { |
296 // TODO(rch): Consider the case where multiple requests are started | 321 // TODO(rch): Consider the case where multiple requests are started |
297 // before the first completes. In this case, only one of the jobs | 322 // before the first completes. In this case, only one of the jobs |
298 // would reach this code, whereas all of them should should have. | 323 // would reach this code, whereas all of them should should have. |
299 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_MAPPING_MISSING); | 324 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_MAPPING_MISSING); |
300 } | 325 } |
| 326 alternate_protocol_map_.Put(server, |
| 327 AlternateProtocols(/*size=*/1, alternate)); |
301 } | 328 } |
302 | 329 |
303 alternate_protocol_map_.Put(server, alternate); | |
304 | |
305 // If this host ends with a canonical suffix, then set it as the | 330 // If this host ends with a canonical suffix, then set it as the |
306 // canonical host. | 331 // canonical host. |
307 for (size_t i = 0; i < canonical_suffixes_.size(); ++i) { | 332 for (size_t i = 0; i < canonical_suffixes_.size(); ++i) { |
308 std::string canonical_suffix = canonical_suffixes_[i]; | 333 std::string canonical_suffix = canonical_suffixes_[i]; |
309 if (EndsWith(server.host(), canonical_suffixes_[i], false)) { | 334 if (EndsWith(server.host(), canonical_suffixes_[i], false)) { |
310 HostPortPair canonical_host(canonical_suffix, server.port()); | 335 HostPortPair canonical_host(canonical_suffix, server.port()); |
311 canonical_host_to_origin_map_[canonical_host] = server; | 336 canonical_host_to_origin_map_[canonical_host] = server; |
312 break; | 337 break; |
313 } | 338 } |
314 } | 339 } |
315 } | 340 } |
316 | 341 |
317 void HttpServerPropertiesImpl::SetBrokenAlternateProtocol( | 342 void HttpServerPropertiesImpl::SetBrokenAlternateProtocol( |
318 const HostPortPair& server) { | 343 const HostPortPair& server, |
319 AlternateProtocolMap::iterator it = alternate_protocol_map_.Get(server); | 344 const AlternateProtocolInfo& broken_alternate_protocol) { |
320 if (it == alternate_protocol_map_.end()) { | 345 if (!IsAlternateProtocolValid(broken_alternate_protocol.protocol)) |
321 if (!HasAlternateProtocol(server)) { | 346 return; |
322 LOG(DFATAL) << "Trying to mark unknown alternate protocol broken."; | 347 AddAlternateProtocol(server, broken_alternate_protocol.port, |
323 return; | 348 broken_alternate_protocol.protocol, |
| 349 broken_alternate_protocol.probability); |
| 350 AlternateProtocolMap::iterator alternate_protocols = |
| 351 alternate_protocol_map_.Get(server); |
| 352 DCHECK(alternate_protocols != alternate_protocol_map_.end()); |
| 353 AlternateProtocols::iterator it; |
| 354 for (it = alternate_protocols->second.begin(); |
| 355 it != alternate_protocols->second.end(); ++it) { |
| 356 if (it->Equals(broken_alternate_protocol)) { |
| 357 it->is_broken = true; |
| 358 break; |
324 } | 359 } |
325 // This server's alternate protocol information is coming from a canonical | |
326 // server. Add an entry in the map for this server explicitly so that | |
327 // it can be marked as broken. | |
328 it = alternate_protocol_map_.Put(server, GetAlternateProtocol(server)); | |
329 } | 360 } |
330 it->second.is_broken = true; | 361 DCHECK(it != alternate_protocols->second.end()); |
331 int count = ++broken_alternate_protocol_map_[server]; | 362 |
| 363 const BrokenAlternateProtocolEntry entry(server, |
| 364 broken_alternate_protocol.port, |
| 365 broken_alternate_protocol.protocol); |
| 366 int count = ++broken_alternate_protocol_map_[entry]; |
332 base::TimeDelta delay = | 367 base::TimeDelta delay = |
333 base::TimeDelta::FromSeconds(kBrokenAlternateProtocolDelaySecs); | 368 base::TimeDelta::FromSeconds(kBrokenAlternateProtocolDelaySecs); |
334 BrokenAlternateProtocolEntry entry; | 369 base::TimeTicks when = base::TimeTicks::Now() + delay * (1 << (count - 1)); |
335 entry.server = server; | 370 broken_alternate_protocol_list_.push_back( |
336 entry.when = base::TimeTicks::Now() + delay * (1 << (count - 1)); | 371 BrokenAlternateProtocolEntryWithTime(entry, when)); |
337 broken_alternate_protocol_list_.push_back(entry); | |
338 | 372 |
339 // Do not leave this host as canonical so that we don't infer the other | 373 // Do not leave this host as canonical so that we don't infer the other |
340 // hosts are also broken without testing them first. | 374 // hosts are also broken without testing them first. |
341 RemoveCanonicalHost(server); | 375 RemoveCanonicalHost(server); |
342 | 376 |
343 // If this is the only entry in the list, schedule an expiration task. | 377 // If this is the only entry in the list, schedule an expiration task. |
344 // Otherwse it will be rescheduled automatically when the pending | 378 // Otherwise it will be rescheduled automatically when the pending task runs. |
345 // task runs. | |
346 if (broken_alternate_protocol_list_.size() == 1) { | 379 if (broken_alternate_protocol_list_.size() == 1) { |
347 ScheduleBrokenAlternateProtocolMappingsExpiration(); | 380 ScheduleBrokenAlternateProtocolMappingsExpiration(); |
348 } | 381 } |
349 } | 382 } |
350 | 383 |
351 bool HttpServerPropertiesImpl::WasAlternateProtocolRecentlyBroken( | 384 bool HttpServerPropertiesImpl::WasAlternateProtocolRecentlyBroken( |
352 const HostPortPair& server) { | 385 const HostPortPair& server, |
353 return ContainsKey(broken_alternate_protocol_map_, server); | 386 const AlternateProtocolInfo& alternate_protocol) { |
| 387 const BrokenAlternateProtocolEntry entry(server, alternate_protocol.port, |
| 388 alternate_protocol.protocol); |
| 389 return ContainsKey(broken_alternate_protocol_map_, entry); |
354 } | 390 } |
355 | 391 |
356 void HttpServerPropertiesImpl::ConfirmAlternateProtocol( | 392 void HttpServerPropertiesImpl::ConfirmAlternateProtocol( |
357 const HostPortPair& server) { | 393 const HostPortPair& server, |
358 broken_alternate_protocol_map_.erase(server); | 394 const AlternateProtocolInfo& alternate_protocol) { |
| 395 const BrokenAlternateProtocolEntry entry(server, alternate_protocol.port, |
| 396 alternate_protocol.protocol); |
| 397 broken_alternate_protocol_map_.erase(entry); |
359 } | 398 } |
360 | 399 |
361 void HttpServerPropertiesImpl::ClearAlternateProtocol( | 400 void HttpServerPropertiesImpl::ClearAlternateProtocol( |
362 const HostPortPair& server) { | 401 const HostPortPair& server) { |
363 AlternateProtocolMap::iterator it = alternate_protocol_map_.Peek(server); | 402 AlternateProtocolMap::iterator it = alternate_protocol_map_.Peek(server); |
364 if (it != alternate_protocol_map_.end()) | 403 if (it != alternate_protocol_map_.end()) |
365 alternate_protocol_map_.Erase(it); | 404 alternate_protocol_map_.Erase(it); |
366 | 405 |
367 RemoveCanonicalHost(server); | 406 RemoveCanonicalHost(server); |
368 } | 407 } |
369 | 408 |
| 409 void HttpServerPropertiesImpl::ClearNonBrokenAlternateProtocols( |
| 410 const HostPortPair& server) { |
| 411 AlternateProtocolMap::iterator alternate_protocols = |
| 412 alternate_protocol_map_.Peek(server); |
| 413 if (alternate_protocols == alternate_protocol_map_.end()) { |
| 414 return; |
| 415 } |
| 416 for (AlternateProtocols::iterator it = alternate_protocols->second.begin(); |
| 417 it != alternate_protocols->second.end();) { |
| 418 if (!it->is_broken) { |
| 419 it = alternate_protocols->second.erase(it); |
| 420 } else { |
| 421 ++it; |
| 422 } |
| 423 } |
| 424 if (alternate_protocols->second.size() == 0) { |
| 425 alternate_protocol_map_.Erase(alternate_protocols); |
| 426 RemoveCanonicalHost(server); |
| 427 } |
| 428 } |
| 429 |
| 430 void HttpServerPropertiesImpl::RemoveAlternateProtocol( |
| 431 const HostPortPair& server, |
| 432 const AlternateProtocolInfo& alternate_protocol) { |
| 433 AlternateProtocolMap::iterator alternate_protocols = |
| 434 alternate_protocol_map_.Peek(server); |
| 435 if (alternate_protocols == alternate_protocol_map_.end()) { |
| 436 return; |
| 437 } |
| 438 for (AlternateProtocols::iterator it = alternate_protocols->second.begin(); |
| 439 it != alternate_protocols->second.end(); ++it) { |
| 440 if (it->EqualsModuloProbability(alternate_protocol)) { |
| 441 alternate_protocols->second.erase(it); |
| 442 break; |
| 443 } |
| 444 } |
| 445 if (alternate_protocols->second.size() == 0) { |
| 446 alternate_protocol_map_.Erase(alternate_protocols); |
| 447 RemoveCanonicalHost(server); |
| 448 } |
| 449 } |
| 450 |
370 const AlternateProtocolMap& | 451 const AlternateProtocolMap& |
371 HttpServerPropertiesImpl::alternate_protocol_map() const { | 452 HttpServerPropertiesImpl::alternate_protocol_map() const { |
372 return alternate_protocol_map_; | 453 return alternate_protocol_map_; |
373 } | 454 } |
374 | 455 |
375 const SettingsMap& HttpServerPropertiesImpl::GetSpdySettings( | 456 const SettingsMap& HttpServerPropertiesImpl::GetSpdySettings( |
376 const HostPortPair& host_port_pair) { | 457 const HostPortPair& host_port_pair) { |
377 SpdySettingsMap::iterator it = spdy_settings_map_.Get(host_port_pair); | 458 SpdySettingsMap::iterator it = spdy_settings_map_.Get(host_port_pair); |
378 if (it == spdy_settings_map_.end()) { | 459 if (it == spdy_settings_map_.end()) { |
379 CR_DEFINE_STATIC_LOCAL(SettingsMap, kEmptySettingsMap, ()); | 460 CR_DEFINE_STATIC_LOCAL(SettingsMap, kEmptySettingsMap, ()); |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
502 | 583 |
503 if (!canonical->second.Equals(server)) | 584 if (!canonical->second.Equals(server)) |
504 return; | 585 return; |
505 | 586 |
506 canonical_host_to_origin_map_.erase(canonical->first); | 587 canonical_host_to_origin_map_.erase(canonical->first); |
507 } | 588 } |
508 | 589 |
509 void HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings() { | 590 void HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings() { |
510 base::TimeTicks now = base::TimeTicks::Now(); | 591 base::TimeTicks now = base::TimeTicks::Now(); |
511 while (!broken_alternate_protocol_list_.empty()) { | 592 while (!broken_alternate_protocol_list_.empty()) { |
512 BrokenAlternateProtocolEntry entry = | 593 BrokenAlternateProtocolEntryWithTime entry_with_time = |
513 broken_alternate_protocol_list_.front(); | 594 broken_alternate_protocol_list_.front(); |
514 if (now < entry.when) { | 595 if (now < entry_with_time.when) { |
515 break; | 596 break; |
516 } | 597 } |
517 | 598 |
518 ClearAlternateProtocol(entry.server); | 599 const BrokenAlternateProtocolEntry& entry = |
| 600 entry_with_time.broken_alternate_protocol_entry; |
| 601 const AlternateProtocolInfo alternate_protocol(entry.port, entry.protocol, |
| 602 1.0); |
| 603 RemoveAlternateProtocol(entry.server, alternate_protocol); |
519 broken_alternate_protocol_list_.pop_front(); | 604 broken_alternate_protocol_list_.pop_front(); |
520 } | 605 } |
521 ScheduleBrokenAlternateProtocolMappingsExpiration(); | 606 ScheduleBrokenAlternateProtocolMappingsExpiration(); |
522 } | 607 } |
523 | 608 |
524 void | 609 void |
525 HttpServerPropertiesImpl::ScheduleBrokenAlternateProtocolMappingsExpiration() { | 610 HttpServerPropertiesImpl::ScheduleBrokenAlternateProtocolMappingsExpiration() { |
526 if (broken_alternate_protocol_list_.empty()) { | 611 if (broken_alternate_protocol_list_.empty()) { |
527 return; | 612 return; |
528 } | 613 } |
529 base::TimeTicks now = base::TimeTicks::Now(); | 614 base::TimeTicks now = base::TimeTicks::Now(); |
530 base::TimeTicks when = broken_alternate_protocol_list_.front().when; | 615 base::TimeTicks when = broken_alternate_protocol_list_.front().when; |
531 base::TimeDelta delay = when > now ? when - now : base::TimeDelta(); | 616 base::TimeDelta delay = when > now ? when - now : base::TimeDelta(); |
532 base::MessageLoop::current()->PostDelayedTask( | 617 base::MessageLoop::current()->PostDelayedTask( |
533 FROM_HERE, | 618 FROM_HERE, |
534 base::Bind( | 619 base::Bind( |
535 &HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings, | 620 &HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings, |
536 weak_ptr_factory_.GetWeakPtr()), | 621 weak_ptr_factory_.GetWeakPtr()), |
537 delay); | 622 delay); |
538 } | 623 } |
539 | 624 |
540 } // namespace net | 625 } // namespace net |
OLD | NEW |