| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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/quic/quic_unacked_packet_map.h" | 5 #include "net/quic/quic_unacked_packet_map.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #include "base/stl_util.h" | 8 #include "base/stl_util.h" |
| 9 #include "net/quic/quic_connection_stats.h" | 9 #include "net/quic/quic_connection_stats.h" |
| 10 #include "net/quic/quic_utils_chromium.h" | 10 #include "net/quic/quic_utils_chromium.h" |
| 11 | 11 |
| 12 using std::max; | 12 using std::max; |
| 13 | 13 |
| 14 namespace net { | 14 namespace net { |
| 15 | 15 |
| 16 QuicUnackedPacketMap::TransmissionInfo::TransmissionInfo() | 16 QuicUnackedPacketMap::TransmissionInfo::TransmissionInfo() |
| 17 : retransmittable_frames(NULL), | 17 : retransmittable_frames(NULL), |
| 18 sequence_number_length(PACKET_1BYTE_SEQUENCE_NUMBER), | 18 sequence_number_length(PACKET_1BYTE_SEQUENCE_NUMBER), |
| 19 sent_time(QuicTime::Zero()), | 19 sent_time(QuicTime::Zero()), |
| 20 bytes_sent(0), | 20 bytes_sent(0), |
| 21 nack_count(0), | 21 nack_count(0), |
| 22 all_transmissions(NULL), | 22 all_transmissions(NULL), |
| 23 pending(false) { } | 23 pending(false) { |
| 24 } |
| 24 | 25 |
| 25 QuicUnackedPacketMap::TransmissionInfo::TransmissionInfo( | 26 QuicUnackedPacketMap::TransmissionInfo::TransmissionInfo( |
| 26 RetransmittableFrames* retransmittable_frames, | 27 RetransmittableFrames* retransmittable_frames, |
| 27 QuicPacketSequenceNumber sequence_number, | 28 QuicPacketSequenceNumber sequence_number, |
| 28 QuicSequenceNumberLength sequence_number_length) | 29 QuicSequenceNumberLength sequence_number_length) |
| 29 : retransmittable_frames(retransmittable_frames), | 30 : retransmittable_frames(retransmittable_frames), |
| 30 sequence_number_length(sequence_number_length), | 31 sequence_number_length(sequence_number_length), |
| 31 sent_time(QuicTime::Zero()), | 32 sent_time(QuicTime::Zero()), |
| 32 bytes_sent(0), | 33 bytes_sent(0), |
| 33 nack_count(0), | 34 nack_count(0), |
| (...skipping 18 matching lines...) Expand all Loading... |
| 52 } | 53 } |
| 53 | 54 |
| 54 QuicUnackedPacketMap::QuicUnackedPacketMap() | 55 QuicUnackedPacketMap::QuicUnackedPacketMap() |
| 55 : largest_sent_packet_(0), | 56 : largest_sent_packet_(0), |
| 56 bytes_in_flight_(0), | 57 bytes_in_flight_(0), |
| 57 pending_crypto_packet_count_(0) { | 58 pending_crypto_packet_count_(0) { |
| 58 } | 59 } |
| 59 | 60 |
| 60 QuicUnackedPacketMap::~QuicUnackedPacketMap() { | 61 QuicUnackedPacketMap::~QuicUnackedPacketMap() { |
| 61 for (UnackedPacketMap::iterator it = unacked_packets_.begin(); | 62 for (UnackedPacketMap::iterator it = unacked_packets_.begin(); |
| 62 it != unacked_packets_.end(); ++it) { | 63 it != unacked_packets_.end(); |
| 64 ++it) { |
| 63 delete it->second.retransmittable_frames; | 65 delete it->second.retransmittable_frames; |
| 64 // Only delete all_transmissions once, for the newest packet. | 66 // Only delete all_transmissions once, for the newest packet. |
| 65 if (it->first == *it->second.all_transmissions->rbegin()) { | 67 if (it->first == *it->second.all_transmissions->rbegin()) { |
| 66 delete it->second.all_transmissions; | 68 delete it->second.all_transmissions; |
| 67 } | 69 } |
| 68 } | 70 } |
| 69 } | 71 } |
| 70 | 72 |
| 71 // TODO(ianswett): Combine this method with OnPacketSent once packets are always | 73 // TODO(ianswett): Combine this method with OnPacketSent once packets are always |
| 72 // sent in order and the connection tracks RetransmittableFrames for longer. | 74 // sent in order and the connection tracks RetransmittableFrames for longer. |
| 73 void QuicUnackedPacketMap::AddPacket( | 75 void QuicUnackedPacketMap::AddPacket( |
| 74 const SerializedPacket& serialized_packet) { | 76 const SerializedPacket& serialized_packet) { |
| 75 if (!unacked_packets_.empty()) { | 77 if (!unacked_packets_.empty()) { |
| 76 bool is_old_packet = unacked_packets_.rbegin()->first >= | 78 bool is_old_packet = |
| 77 serialized_packet.sequence_number; | 79 unacked_packets_.rbegin()->first >= serialized_packet.sequence_number; |
| 78 LOG_IF(DFATAL, is_old_packet) << "Old packet serialized: " | 80 LOG_IF(DFATAL, is_old_packet) |
| 79 << serialized_packet.sequence_number | 81 << "Old packet serialized: " << serialized_packet.sequence_number |
| 80 << " vs: " | 82 << " vs: " << unacked_packets_.rbegin()->first; |
| 81 << unacked_packets_.rbegin()->first; | |
| 82 } | 83 } |
| 83 | 84 |
| 84 unacked_packets_[serialized_packet.sequence_number] = | 85 unacked_packets_[serialized_packet.sequence_number] = |
| 85 TransmissionInfo(serialized_packet.retransmittable_frames, | 86 TransmissionInfo(serialized_packet.retransmittable_frames, |
| 86 serialized_packet.sequence_number, | 87 serialized_packet.sequence_number, |
| 87 serialized_packet.sequence_number_length); | 88 serialized_packet.sequence_number_length); |
| 88 if (serialized_packet.retransmittable_frames != NULL && | 89 if (serialized_packet.retransmittable_frames != NULL && |
| 89 serialized_packet.retransmittable_frames->HasCryptoHandshake() | 90 serialized_packet.retransmittable_frames->HasCryptoHandshake() == |
| 90 == IS_HANDSHAKE) { | 91 IS_HANDSHAKE) { |
| 91 ++pending_crypto_packet_count_; | 92 ++pending_crypto_packet_count_; |
| 92 } | 93 } |
| 93 } | 94 } |
| 94 | 95 |
| 95 void QuicUnackedPacketMap::OnRetransmittedPacket( | 96 void QuicUnackedPacketMap::OnRetransmittedPacket( |
| 96 QuicPacketSequenceNumber old_sequence_number, | 97 QuicPacketSequenceNumber old_sequence_number, |
| 97 QuicPacketSequenceNumber new_sequence_number) { | 98 QuicPacketSequenceNumber new_sequence_number) { |
| 98 DCHECK(ContainsKey(unacked_packets_, old_sequence_number)); | 99 DCHECK(ContainsKey(unacked_packets_, old_sequence_number)); |
| 99 DCHECK(unacked_packets_.empty() || | 100 DCHECK(unacked_packets_.empty() || |
| 100 unacked_packets_.rbegin()->first < new_sequence_number); | 101 unacked_packets_.rbegin()->first < new_sequence_number); |
| 101 | 102 |
| 102 // TODO(ianswett): Discard and lose the packet lazily instead of immediately. | 103 // TODO(ianswett): Discard and lose the packet lazily instead of immediately. |
| 103 TransmissionInfo* transmission_info = | 104 TransmissionInfo* transmission_info = |
| 104 FindOrNull(unacked_packets_, old_sequence_number); | 105 FindOrNull(unacked_packets_, old_sequence_number); |
| 105 RetransmittableFrames* frames = transmission_info->retransmittable_frames; | 106 RetransmittableFrames* frames = transmission_info->retransmittable_frames; |
| 106 LOG_IF(DFATAL, frames == NULL) << "Attempt to retransmit packet with no " | 107 LOG_IF(DFATAL, frames == NULL) |
| 107 << "retransmittable frames: " | 108 << "Attempt to retransmit packet with no " |
| 108 << old_sequence_number; | 109 << "retransmittable frames: " << old_sequence_number; |
| 109 | 110 |
| 110 // We keep the old packet in the unacked packet list until it, or one of | 111 // We keep the old packet in the unacked packet list until it, or one of |
| 111 // the retransmissions of it are acked. | 112 // the retransmissions of it are acked. |
| 112 transmission_info->retransmittable_frames = NULL; | 113 transmission_info->retransmittable_frames = NULL; |
| 113 unacked_packets_[new_sequence_number] = | 114 unacked_packets_[new_sequence_number] = |
| 114 TransmissionInfo(frames, | 115 TransmissionInfo(frames, |
| 115 new_sequence_number, | 116 new_sequence_number, |
| 116 transmission_info->sequence_number_length, | 117 transmission_info->sequence_number_length, |
| 117 transmission_info->all_transmissions); | 118 transmission_info->all_transmissions); |
| 118 } | 119 } |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 163 if (it == unacked_packets_.end()) { | 164 if (it == unacked_packets_.end()) { |
| 164 LOG(DFATAL) << "packet is not unacked: " << sequence_number; | 165 LOG(DFATAL) << "packet is not unacked: " << sequence_number; |
| 165 return; | 166 return; |
| 166 } | 167 } |
| 167 const TransmissionInfo& transmission_info = it->second; | 168 const TransmissionInfo& transmission_info = it->second; |
| 168 transmission_info.all_transmissions->erase(sequence_number); | 169 transmission_info.all_transmissions->erase(sequence_number); |
| 169 if (transmission_info.all_transmissions->empty()) { | 170 if (transmission_info.all_transmissions->empty()) { |
| 170 delete transmission_info.all_transmissions; | 171 delete transmission_info.all_transmissions; |
| 171 } | 172 } |
| 172 if (transmission_info.retransmittable_frames != NULL) { | 173 if (transmission_info.retransmittable_frames != NULL) { |
| 173 if (transmission_info.retransmittable_frames->HasCryptoHandshake() | 174 if (transmission_info.retransmittable_frames->HasCryptoHandshake() == |
| 174 == IS_HANDSHAKE) { | 175 IS_HANDSHAKE) { |
| 175 --pending_crypto_packet_count_; | 176 --pending_crypto_packet_count_; |
| 176 } | 177 } |
| 177 delete transmission_info.retransmittable_frames; | 178 delete transmission_info.retransmittable_frames; |
| 178 } | 179 } |
| 179 unacked_packets_.erase(it); | 180 unacked_packets_.erase(it); |
| 180 } | 181 } |
| 181 | 182 |
| 182 void QuicUnackedPacketMap::NeuterPacket( | 183 void QuicUnackedPacketMap::NeuterPacket( |
| 183 QuicPacketSequenceNumber sequence_number) { | 184 QuicPacketSequenceNumber sequence_number) { |
| 184 UnackedPacketMap::iterator it = unacked_packets_.find(sequence_number); | 185 UnackedPacketMap::iterator it = unacked_packets_.find(sequence_number); |
| 185 if (it == unacked_packets_.end()) { | 186 if (it == unacked_packets_.end()) { |
| 186 LOG(DFATAL) << "packet is not unacked: " << sequence_number; | 187 LOG(DFATAL) << "packet is not unacked: " << sequence_number; |
| 187 return; | 188 return; |
| 188 } | 189 } |
| 189 TransmissionInfo* transmission_info = &it->second; | 190 TransmissionInfo* transmission_info = &it->second; |
| 190 if (transmission_info->all_transmissions->size() > 1) { | 191 if (transmission_info->all_transmissions->size() > 1) { |
| 191 transmission_info->all_transmissions->erase(sequence_number); | 192 transmission_info->all_transmissions->erase(sequence_number); |
| 192 transmission_info->all_transmissions = new SequenceNumberSet(); | 193 transmission_info->all_transmissions = new SequenceNumberSet(); |
| 193 transmission_info->all_transmissions->insert(sequence_number); | 194 transmission_info->all_transmissions->insert(sequence_number); |
| 194 } | 195 } |
| 195 if (transmission_info->retransmittable_frames != NULL) { | 196 if (transmission_info->retransmittable_frames != NULL) { |
| 196 if (transmission_info->retransmittable_frames->HasCryptoHandshake() | 197 if (transmission_info->retransmittable_frames->HasCryptoHandshake() == |
| 197 == IS_HANDSHAKE) { | 198 IS_HANDSHAKE) { |
| 198 --pending_crypto_packet_count_; | 199 --pending_crypto_packet_count_; |
| 199 } | 200 } |
| 200 delete transmission_info->retransmittable_frames; | 201 delete transmission_info->retransmittable_frames; |
| 201 transmission_info->retransmittable_frames = NULL; | 202 transmission_info->retransmittable_frames = NULL; |
| 202 } | 203 } |
| 203 } | 204 } |
| 204 | 205 |
| 205 // static | 206 // static |
| 206 bool QuicUnackedPacketMap::IsSentAndNotPending( | 207 bool QuicUnackedPacketMap::IsSentAndNotPending( |
| 207 const TransmissionInfo& transmission_info) { | 208 const TransmissionInfo& transmission_info) { |
| 208 return !transmission_info.pending && | 209 return !transmission_info.pending && |
| 209 transmission_info.sent_time != QuicTime::Zero() && | 210 transmission_info.sent_time != QuicTime::Zero() && |
| 210 transmission_info.bytes_sent == 0; | 211 transmission_info.bytes_sent == 0; |
| 211 } | 212 } |
| 212 | 213 |
| 213 bool QuicUnackedPacketMap::IsUnacked( | 214 bool QuicUnackedPacketMap::IsUnacked( |
| 214 QuicPacketSequenceNumber sequence_number) const { | 215 QuicPacketSequenceNumber sequence_number) const { |
| 215 return ContainsKey(unacked_packets_, sequence_number); | 216 return ContainsKey(unacked_packets_, sequence_number); |
| 216 } | 217 } |
| 217 | 218 |
| 218 bool QuicUnackedPacketMap::IsPending( | 219 bool QuicUnackedPacketMap::IsPending( |
| 219 QuicPacketSequenceNumber sequence_number) const { | 220 QuicPacketSequenceNumber sequence_number) const { |
| 220 const TransmissionInfo* transmission_info = | 221 const TransmissionInfo* transmission_info = |
| (...skipping 14 matching lines...) Expand all Loading... |
| 235 bytes_in_flight_ -= it->second.bytes_sent; | 236 bytes_in_flight_ -= it->second.bytes_sent; |
| 236 it->second.pending = false; | 237 it->second.pending = false; |
| 237 } | 238 } |
| 238 } | 239 } |
| 239 | 240 |
| 240 bool QuicUnackedPacketMap::HasUnackedPackets() const { | 241 bool QuicUnackedPacketMap::HasUnackedPackets() const { |
| 241 return !unacked_packets_.empty(); | 242 return !unacked_packets_.empty(); |
| 242 } | 243 } |
| 243 | 244 |
| 244 bool QuicUnackedPacketMap::HasPendingPackets() const { | 245 bool QuicUnackedPacketMap::HasPendingPackets() const { |
| 245 for (UnackedPacketMap::const_reverse_iterator it = | 246 for (UnackedPacketMap::const_reverse_iterator it = unacked_packets_.rbegin(); |
| 246 unacked_packets_.rbegin(); it != unacked_packets_.rend(); ++it) { | 247 it != unacked_packets_.rend(); |
| 248 ++it) { |
| 247 if (it->second.pending) { | 249 if (it->second.pending) { |
| 248 return true; | 250 return true; |
| 249 } | 251 } |
| 250 } | 252 } |
| 251 return false; | 253 return false; |
| 252 } | 254 } |
| 253 | 255 |
| 254 const QuicUnackedPacketMap::TransmissionInfo& | 256 const QuicUnackedPacketMap::TransmissionInfo& |
| 255 QuicUnackedPacketMap::GetTransmissionInfo( | 257 QuicUnackedPacketMap::GetTransmissionInfo( |
| 256 QuicPacketSequenceNumber sequence_number) const { | 258 QuicPacketSequenceNumber sequence_number) const { |
| 257 return unacked_packets_.find(sequence_number)->second; | 259 return unacked_packets_.find(sequence_number)->second; |
| 258 } | 260 } |
| 259 | 261 |
| 260 QuicTime QuicUnackedPacketMap::GetLastPacketSentTime() const { | 262 QuicTime QuicUnackedPacketMap::GetLastPacketSentTime() const { |
| 261 UnackedPacketMap::const_reverse_iterator it = unacked_packets_.rbegin(); | 263 UnackedPacketMap::const_reverse_iterator it = unacked_packets_.rbegin(); |
| 262 while (it != unacked_packets_.rend()) { | 264 while (it != unacked_packets_.rend()) { |
| 263 if (it->second.pending) { | 265 if (it->second.pending) { |
| 264 LOG_IF(DFATAL, it->second.sent_time == QuicTime::Zero()) | 266 LOG_IF(DFATAL, it->second.sent_time == QuicTime::Zero()) |
| 265 << "Sent time can never be zero for a pending packet."; | 267 << "Sent time can never be zero for a pending packet."; |
| 266 return it->second.sent_time; | 268 return it->second.sent_time; |
| (...skipping 17 matching lines...) Expand all Loading... |
| 284 return it->second.sent_time; | 286 return it->second.sent_time; |
| 285 } | 287 } |
| 286 | 288 |
| 287 size_t QuicUnackedPacketMap::GetNumUnackedPackets() const { | 289 size_t QuicUnackedPacketMap::GetNumUnackedPackets() const { |
| 288 return unacked_packets_.size(); | 290 return unacked_packets_.size(); |
| 289 } | 291 } |
| 290 | 292 |
| 291 bool QuicUnackedPacketMap::HasMultiplePendingPackets() const { | 293 bool QuicUnackedPacketMap::HasMultiplePendingPackets() const { |
| 292 size_t num_pending = 0; | 294 size_t num_pending = 0; |
| 293 for (UnackedPacketMap::const_reverse_iterator it = unacked_packets_.rbegin(); | 295 for (UnackedPacketMap::const_reverse_iterator it = unacked_packets_.rbegin(); |
| 294 it != unacked_packets_.rend(); ++it) { | 296 it != unacked_packets_.rend(); |
| 297 ++it) { |
| 295 if (it->second.pending) { | 298 if (it->second.pending) { |
| 296 ++num_pending; | 299 ++num_pending; |
| 297 } | 300 } |
| 298 if (num_pending > 1) { | 301 if (num_pending > 1) { |
| 299 return true; | 302 return true; |
| 300 } | 303 } |
| 301 } | 304 } |
| 302 return false; | 305 return false; |
| 303 } | 306 } |
| 304 | 307 |
| 305 bool QuicUnackedPacketMap::HasPendingCryptoPackets() const { | 308 bool QuicUnackedPacketMap::HasPendingCryptoPackets() const { |
| 306 return pending_crypto_packet_count_ > 0; | 309 return pending_crypto_packet_count_ > 0; |
| 307 } | 310 } |
| 308 | 311 |
| 309 bool QuicUnackedPacketMap::HasUnackedRetransmittableFrames() const { | 312 bool QuicUnackedPacketMap::HasUnackedRetransmittableFrames() const { |
| 310 for (UnackedPacketMap::const_reverse_iterator it = | 313 for (UnackedPacketMap::const_reverse_iterator it = unacked_packets_.rbegin(); |
| 311 unacked_packets_.rbegin(); it != unacked_packets_.rend(); ++it) { | 314 it != unacked_packets_.rend(); |
| 315 ++it) { |
| 312 if (it->second.pending && it->second.retransmittable_frames) { | 316 if (it->second.pending && it->second.retransmittable_frames) { |
| 313 return true; | 317 return true; |
| 314 } | 318 } |
| 315 } | 319 } |
| 316 return false; | 320 return false; |
| 317 } | 321 } |
| 318 | 322 |
| 319 size_t QuicUnackedPacketMap::GetNumRetransmittablePackets() const { | 323 size_t QuicUnackedPacketMap::GetNumRetransmittablePackets() const { |
| 320 size_t num_unacked_packets = 0; | 324 size_t num_unacked_packets = 0; |
| 321 for (UnackedPacketMap::const_iterator it = unacked_packets_.begin(); | 325 for (UnackedPacketMap::const_iterator it = unacked_packets_.begin(); |
| 322 it != unacked_packets_.end(); ++it) { | 326 it != unacked_packets_.end(); |
| 327 ++it) { |
| 323 if (it->second.retransmittable_frames != NULL) { | 328 if (it->second.retransmittable_frames != NULL) { |
| 324 ++num_unacked_packets; | 329 ++num_unacked_packets; |
| 325 } | 330 } |
| 326 } | 331 } |
| 327 return num_unacked_packets; | 332 return num_unacked_packets; |
| 328 } | 333 } |
| 329 | 334 |
| 330 QuicPacketSequenceNumber | 335 QuicPacketSequenceNumber QuicUnackedPacketMap::GetLeastUnackedSentPacket() |
| 331 QuicUnackedPacketMap::GetLeastUnackedSentPacket() const { | 336 const { |
| 332 if (unacked_packets_.empty()) { | 337 if (unacked_packets_.empty()) { |
| 333 // If there are no unacked packets, return 0. | 338 // If there are no unacked packets, return 0. |
| 334 return 0; | 339 return 0; |
| 335 } | 340 } |
| 336 | 341 |
| 337 return unacked_packets_.begin()->first; | 342 return unacked_packets_.begin()->first; |
| 338 } | 343 } |
| 339 | 344 |
| 340 SequenceNumberSet QuicUnackedPacketMap::GetUnackedPackets() const { | 345 SequenceNumberSet QuicUnackedPacketMap::GetUnackedPackets() const { |
| 341 SequenceNumberSet unacked_packets; | 346 SequenceNumberSet unacked_packets; |
| 342 for (UnackedPacketMap::const_iterator it = unacked_packets_.begin(); | 347 for (UnackedPacketMap::const_iterator it = unacked_packets_.begin(); |
| 343 it != unacked_packets_.end(); ++it) { | 348 it != unacked_packets_.end(); |
| 349 ++it) { |
| 344 unacked_packets.insert(it->first); | 350 unacked_packets.insert(it->first); |
| 345 } | 351 } |
| 346 return unacked_packets; | 352 return unacked_packets; |
| 347 } | 353 } |
| 348 | 354 |
| 349 void QuicUnackedPacketMap::SetSent(QuicPacketSequenceNumber sequence_number, | 355 void QuicUnackedPacketMap::SetSent(QuicPacketSequenceNumber sequence_number, |
| 350 QuicTime sent_time, | 356 QuicTime sent_time, |
| 351 QuicByteCount bytes_sent, | 357 QuicByteCount bytes_sent, |
| 352 bool set_pending) { | 358 bool set_pending) { |
| 353 DCHECK_LT(0u, sequence_number); | 359 DCHECK_LT(0u, sequence_number); |
| 354 UnackedPacketMap::iterator it = unacked_packets_.find(sequence_number); | 360 UnackedPacketMap::iterator it = unacked_packets_.find(sequence_number); |
| 355 if (it == unacked_packets_.end()) { | 361 if (it == unacked_packets_.end()) { |
| 356 LOG(DFATAL) << "OnPacketSent called for packet that is not unacked: " | 362 LOG(DFATAL) << "OnPacketSent called for packet that is not unacked: " |
| 357 << sequence_number; | 363 << sequence_number; |
| 358 return; | 364 return; |
| 359 } | 365 } |
| 360 DCHECK(!it->second.pending); | 366 DCHECK(!it->second.pending); |
| 361 | 367 |
| 362 largest_sent_packet_ = max(sequence_number, largest_sent_packet_); | 368 largest_sent_packet_ = max(sequence_number, largest_sent_packet_); |
| 363 it->second.sent_time = sent_time; | 369 it->second.sent_time = sent_time; |
| 364 if (set_pending) { | 370 if (set_pending) { |
| 365 bytes_in_flight_ += bytes_sent; | 371 bytes_in_flight_ += bytes_sent; |
| 366 it->second.bytes_sent = bytes_sent; | 372 it->second.bytes_sent = bytes_sent; |
| 367 it->second.pending = true; | 373 it->second.pending = true; |
| 368 } | 374 } |
| 369 } | 375 } |
| 370 | 376 |
| 371 } // namespace net | 377 } // namespace net |
| OLD | NEW |