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/quic/quic_connection.h" | 5 #include "net/quic/quic_connection.h" |
6 | 6 |
7 #include <string.h> | 7 #include <string.h> |
8 #include <sys/types.h> | 8 #include <sys/types.h> |
9 #include <algorithm> | 9 #include <algorithm> |
10 #include <iterator> | 10 #include <iterator> |
11 #include <limits> | 11 #include <limits> |
12 #include <memory> | 12 #include <memory> |
13 #include <set> | 13 #include <set> |
14 #include <utility> | 14 #include <utility> |
15 | 15 |
16 #include "base/logging.h" | 16 #include "base/logging.h" |
17 #include "base/rand_util.h" | 17 #include "base/rand_util.h" |
18 #include "base/sha1.h" | 18 #include "base/sha1.h" |
19 #include "base/stl_util.h" | 19 #include "base/stl_util.h" |
20 #include "net/quic/crypto/quic_decrypter.h" | 20 #include "net/quic/crypto/quic_decrypter.h" |
21 #include "net/quic/crypto/quic_encrypter.h" | 21 #include "net/quic/crypto/quic_encrypter.h" |
22 #include "net/quic/quic_ack_notifier_manager.h" | |
23 #include "net/quic/quic_bandwidth.h" | 22 #include "net/quic/quic_bandwidth.h" |
24 #include "net/quic/quic_utils.h" | 23 #include "net/quic/quic_utils.h" |
25 | 24 |
26 using base::hash_map; | 25 using base::hash_map; |
27 using base::hash_set; | 26 using base::hash_set; |
28 using base::StringPiece; | 27 using base::StringPiece; |
29 using std::list; | 28 using std::list; |
30 using std::make_pair; | 29 using std::make_pair; |
31 using std::min; | 30 using std::min; |
32 using std::max; | 31 using std::max; |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
130 | 129 |
131 // An alarm that is scheduled when the sent scheduler requires a | 130 // An alarm that is scheduled when the sent scheduler requires a |
132 // a delay before sending packets and fires when the packet may be sent. | 131 // a delay before sending packets and fires when the packet may be sent. |
133 class SendAlarm : public QuicAlarm::Delegate { | 132 class SendAlarm : public QuicAlarm::Delegate { |
134 public: | 133 public: |
135 explicit SendAlarm(QuicConnection* connection) | 134 explicit SendAlarm(QuicConnection* connection) |
136 : connection_(connection) { | 135 : connection_(connection) { |
137 } | 136 } |
138 | 137 |
139 virtual QuicTime OnAlarm() OVERRIDE { | 138 virtual QuicTime OnAlarm() OVERRIDE { |
140 connection_->OnCanWrite(); | 139 connection_->WriteIfNotBlocked(); |
141 // Never reschedule the alarm, since OnCanWrite does that. | 140 // Never reschedule the alarm, since OnCanWrite does that. |
142 return QuicTime::Zero(); | 141 return QuicTime::Zero(); |
143 } | 142 } |
144 | 143 |
145 private: | 144 private: |
146 QuicConnection* connection_; | 145 QuicConnection* connection_; |
147 }; | 146 }; |
148 | 147 |
149 class TimeoutAlarm : public QuicAlarm::Delegate { | 148 class TimeoutAlarm : public QuicAlarm::Delegate { |
150 public: | 149 public: |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
214 clock_(helper->GetClock()), | 213 clock_(helper->GetClock()), |
215 random_generator_(helper->GetRandomGenerator()), | 214 random_generator_(helper->GetRandomGenerator()), |
216 guid_(guid), | 215 guid_(guid), |
217 peer_address_(address), | 216 peer_address_(address), |
218 largest_seen_packet_with_ack_(0), | 217 largest_seen_packet_with_ack_(0), |
219 write_blocked_(false), | 218 write_blocked_(false), |
220 ack_alarm_(helper->CreateAlarm(new AckAlarm(this))), | 219 ack_alarm_(helper->CreateAlarm(new AckAlarm(this))), |
221 retransmission_alarm_(helper->CreateAlarm(new RetransmissionAlarm(this))), | 220 retransmission_alarm_(helper->CreateAlarm(new RetransmissionAlarm(this))), |
222 abandon_fec_alarm_(helper->CreateAlarm(new AbandonFecAlarm(this))), | 221 abandon_fec_alarm_(helper->CreateAlarm(new AbandonFecAlarm(this))), |
223 send_alarm_(helper->CreateAlarm(new SendAlarm(this))), | 222 send_alarm_(helper->CreateAlarm(new SendAlarm(this))), |
| 223 resume_writes_alarm_(helper->CreateAlarm(new SendAlarm(this))), |
224 timeout_alarm_(helper->CreateAlarm(new TimeoutAlarm(this))), | 224 timeout_alarm_(helper->CreateAlarm(new TimeoutAlarm(this))), |
225 debug_visitor_(NULL), | 225 debug_visitor_(NULL), |
226 packet_creator_(guid_, &framer_, random_generator_, is_server), | 226 packet_creator_(guid_, &framer_, random_generator_, is_server), |
227 packet_generator_(this, NULL, &packet_creator_), | 227 packet_generator_(this, NULL, &packet_creator_), |
228 idle_network_timeout_( | 228 idle_network_timeout_( |
229 QuicTime::Delta::FromSeconds(kDefaultInitialTimeoutSecs)), | 229 QuicTime::Delta::FromSeconds(kDefaultInitialTimeoutSecs)), |
230 overall_connection_timeout_(QuicTime::Delta::Infinite()), | 230 overall_connection_timeout_(QuicTime::Delta::Infinite()), |
231 creation_time_(clock_->ApproximateNow()), | 231 creation_time_(clock_->ApproximateNow()), |
232 time_of_last_received_packet_(clock_->ApproximateNow()), | 232 time_of_last_received_packet_(clock_->ApproximateNow()), |
233 time_of_last_sent_packet_(clock_->ApproximateNow()), | 233 time_of_last_sent_packet_(clock_->ApproximateNow()), |
| 234 sequence_number_of_last_inorder_packet_(0), |
234 congestion_manager_(clock_, kTCP), | 235 congestion_manager_(clock_, kTCP), |
235 sent_packet_manager_(is_server, this), | 236 sent_packet_manager_(is_server, this), |
236 version_negotiation_state_(START_NEGOTIATION), | 237 version_negotiation_state_(START_NEGOTIATION), |
237 consecutive_rto_count_(0), | 238 consecutive_rto_count_(0), |
238 is_server_(is_server), | 239 is_server_(is_server), |
239 connected_(true), | 240 connected_(true), |
240 received_truncated_ack_(false), | 241 received_truncated_ack_(false), |
241 send_ack_in_response_to_packet_(false), | 242 send_ack_in_response_to_packet_(false), |
242 address_migrating_(false) { | 243 address_migrating_(false) { |
243 helper_->SetConnection(this); | 244 helper_->SetConnection(this); |
(...skipping 271 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
515 CloseFecGroupsBefore(incoming_ack.sent_info.least_unacked + 1); | 516 CloseFecGroupsBefore(incoming_ack.sent_info.least_unacked + 1); |
516 | 517 |
517 sent_entropy_manager_.ClearEntropyBefore( | 518 sent_entropy_manager_.ClearEntropyBefore( |
518 received_packet_manager_.least_packet_awaited_by_peer() - 1); | 519 received_packet_manager_.least_packet_awaited_by_peer() - 1); |
519 | 520 |
520 retransmitted_nacked_packet_count_ = 0; | 521 retransmitted_nacked_packet_count_ = 0; |
521 SequenceNumberSet acked_packets; | 522 SequenceNumberSet acked_packets; |
522 sent_packet_manager_.OnIncomingAck( | 523 sent_packet_manager_.OnIncomingAck( |
523 incoming_ack.received_info, received_truncated_ack_, &acked_packets); | 524 incoming_ack.received_info, received_truncated_ack_, &acked_packets); |
524 if (acked_packets.size() > 0) { | 525 if (acked_packets.size() > 0) { |
525 // The AckNotifierManager should be informed of every ACKed sequence number. | |
526 ack_notifier_manager_.OnIncomingAck(acked_packets); | |
527 | |
528 // Reset the RTO timeout for each packet when an ack is received. | 526 // Reset the RTO timeout for each packet when an ack is received. |
529 if (retransmission_alarm_->IsSet()) { | 527 if (retransmission_alarm_->IsSet()) { |
530 retransmission_alarm_->Cancel(); | 528 retransmission_alarm_->Cancel(); |
531 // Only reschedule the timer if there are outstanding packets. | 529 // Only reschedule the timer if there are outstanding packets. |
532 if (sent_packet_manager_.HasUnackedPackets()) { | 530 if (sent_packet_manager_.HasUnackedPackets()) { |
533 QuicTime::Delta retransmission_delay = | 531 QuicTime::Delta retransmission_delay = |
534 congestion_manager_.GetRetransmissionDelay( | 532 congestion_manager_.GetRetransmissionDelay( |
535 sent_packet_manager_.GetNumUnackedPackets(), 0); | 533 sent_packet_manager_.GetNumUnackedPackets(), 0); |
536 retransmission_alarm_->Set(clock_->ApproximateNow().Add( | 534 retransmission_alarm_->Set(clock_->ApproximateNow().Add( |
537 retransmission_delay)); | 535 retransmission_delay)); |
(...skipping 373 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
911 if (!fin && iov_count == 0) { | 909 if (!fin && iov_count == 0) { |
912 LOG(DFATAL) << "Attempt to send empty stream frame"; | 910 LOG(DFATAL) << "Attempt to send empty stream frame"; |
913 } | 911 } |
914 | 912 |
915 // This notifier will be owned by the AckNotifierManager (or deleted below if | 913 // This notifier will be owned by the AckNotifierManager (or deleted below if |
916 // no data was consumed). | 914 // no data was consumed). |
917 QuicAckNotifier* notifier = new QuicAckNotifier(delegate); | 915 QuicAckNotifier* notifier = new QuicAckNotifier(delegate); |
918 QuicConsumedData consumed_data = | 916 QuicConsumedData consumed_data = |
919 SendvStreamDataInner(id, iov, iov_count, offset, fin, notifier); | 917 SendvStreamDataInner(id, iov, iov_count, offset, fin, notifier); |
920 | 918 |
921 if (consumed_data.bytes_consumed > 0) { | 919 if (consumed_data.bytes_consumed == 0) { |
922 // If some data was consumed, then the delegate should be registered for | |
923 // notification when the data is ACKed. | |
924 ack_notifier_manager_.AddAckNotifier(notifier); | |
925 DLOG(INFO) << "Registered AckNotifier."; | |
926 } else { | |
927 // No data was consumed, delete the notifier. | 920 // No data was consumed, delete the notifier. |
928 delete notifier; | 921 delete notifier; |
929 } | 922 } |
930 | 923 |
931 return consumed_data; | 924 return consumed_data; |
932 } | 925 } |
933 | 926 |
934 void QuicConnection::SendRstStream(QuicStreamId id, | 927 void QuicConnection::SendRstStream(QuicStreamId id, |
935 QuicRstStreamErrorCode error) { | 928 QuicRstStreamErrorCode error) { |
936 LOG(INFO) << "Sending RST_STREAM: " << id << " code: " << error; | 929 LOG(INFO) << "Sending RST_STREAM: " << id << " code: " << error; |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1020 pending_handshake)) { | 1013 pending_handshake)) { |
1021 // Set |include_ack| to false in bundler; ack inclusion happens elsewhere. | 1014 // Set |include_ack| to false in bundler; ack inclusion happens elsewhere. |
1022 scoped_ptr<ScopedPacketBundler> bundler( | 1015 scoped_ptr<ScopedPacketBundler> bundler( |
1023 new ScopedPacketBundler(this, false)); | 1016 new ScopedPacketBundler(this, false)); |
1024 bool all_bytes_written = visitor_->OnCanWrite(); | 1017 bool all_bytes_written = visitor_->OnCanWrite(); |
1025 bundler.reset(); | 1018 bundler.reset(); |
1026 // After the visitor writes, it may have caused the socket to become write | 1019 // After the visitor writes, it may have caused the socket to become write |
1027 // blocked or the congestion manager to prohibit sending, so check again. | 1020 // blocked or the congestion manager to prohibit sending, so check again. |
1028 pending_handshake = visitor_->HasPendingHandshake() ? IS_HANDSHAKE | 1021 pending_handshake = visitor_->HasPendingHandshake() ? IS_HANDSHAKE |
1029 : NOT_HANDSHAKE; | 1022 : NOT_HANDSHAKE; |
1030 if (!write_blocked_ && !all_bytes_written && | 1023 if (!all_bytes_written && !resume_writes_alarm_->IsSet() && |
1031 CanWrite(NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, | 1024 CanWrite(NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, |
1032 pending_handshake)) { | 1025 pending_handshake)) { |
1033 // We're not write blocked, but some stream didn't write out all of its | 1026 // We're not write blocked, but some stream didn't write out all of its |
1034 // bytes. Register for 'immediate' resumption so we'll keep writing after | 1027 // bytes. Register for 'immediate' resumption so we'll keep writing after |
1035 // other quic connections have had a chance to use the socket. | 1028 // other quic connections have had a chance to use the socket. |
1036 send_alarm_->Cancel(); | 1029 resume_writes_alarm_->Set(clock_->ApproximateNow()); |
1037 send_alarm_->Set(clock_->ApproximateNow()); | |
1038 } | 1030 } |
1039 } | 1031 } |
1040 | 1032 |
1041 return !write_blocked_; | 1033 return !write_blocked_; |
1042 } | 1034 } |
1043 | 1035 |
1044 bool QuicConnection::ProcessValidatedPacket() { | 1036 bool QuicConnection::ProcessValidatedPacket() { |
1045 if (address_migrating_) { | 1037 if (address_migrating_) { |
1046 SendConnectionCloseWithDetails( | 1038 SendConnectionCloseWithDetails( |
1047 QUIC_ERROR_MIGRATING_ADDRESS, | 1039 QUIC_ERROR_MIGRATING_ADDRESS, |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1093 // Retransmitted packets use the same sequence number length as the | 1085 // Retransmitted packets use the same sequence number length as the |
1094 // original. | 1086 // original. |
1095 // Flush the packet creator before making a new packet. | 1087 // Flush the packet creator before making a new packet. |
1096 // TODO(ianswett): Implement ReserializeAllFrames as a separate path that | 1088 // TODO(ianswett): Implement ReserializeAllFrames as a separate path that |
1097 // does not require the creator to be flushed. | 1089 // does not require the creator to be flushed. |
1098 Flush(); | 1090 Flush(); |
1099 SerializedPacket serialized_packet = packet_creator_.ReserializeAllFrames( | 1091 SerializedPacket serialized_packet = packet_creator_.ReserializeAllFrames( |
1100 pending.retransmittable_frames.frames(), | 1092 pending.retransmittable_frames.frames(), |
1101 pending.sequence_number_length); | 1093 pending.sequence_number_length); |
1102 | 1094 |
1103 // A notifier may be waiting to hear about ACKs for the original sequence | |
1104 // number. Inform them that the sequence number has changed. | |
1105 ack_notifier_manager_.UpdateSequenceNumber( | |
1106 pending.sequence_number, serialized_packet.sequence_number); | |
1107 | |
1108 DLOG(INFO) << ENDPOINT << "Retransmitting " << pending.sequence_number | 1095 DLOG(INFO) << ENDPOINT << "Retransmitting " << pending.sequence_number |
1109 << " as " << serialized_packet.sequence_number; | 1096 << " as " << serialized_packet.sequence_number; |
1110 if (debug_visitor_) { | 1097 if (debug_visitor_) { |
1111 debug_visitor_->OnPacketRetransmitted( | 1098 debug_visitor_->OnPacketRetransmitted( |
1112 pending.sequence_number, serialized_packet.sequence_number); | 1099 pending.sequence_number, serialized_packet.sequence_number); |
1113 } | 1100 } |
1114 sent_packet_manager_.OnRetransmittedPacket( | 1101 sent_packet_manager_.OnRetransmittedPacket( |
1115 pending.sequence_number, serialized_packet.sequence_number); | 1102 pending.sequence_number, serialized_packet.sequence_number); |
1116 | 1103 |
1117 SendOrQueuePacket(pending.retransmittable_frames.encryption_level(), | 1104 SendOrQueuePacket(pending.retransmittable_frames.encryption_level(), |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1179 if (handshake == IS_HANDSHAKE) { | 1166 if (handshake == IS_HANDSHAKE) { |
1180 return true; | 1167 return true; |
1181 } | 1168 } |
1182 | 1169 |
1183 return CanWrite(transmission_type, retransmittable, handshake); | 1170 return CanWrite(transmission_type, retransmittable, handshake); |
1184 } | 1171 } |
1185 | 1172 |
1186 bool QuicConnection::CanWrite(TransmissionType transmission_type, | 1173 bool QuicConnection::CanWrite(TransmissionType transmission_type, |
1187 HasRetransmittableData retransmittable, | 1174 HasRetransmittableData retransmittable, |
1188 IsHandshake handshake) { | 1175 IsHandshake handshake) { |
1189 // TODO(ianswett): If the packet is a retransmit, the current send alarm may | 1176 // This check assumes that if the send alarm is set, it applies equally to all |
1190 // be too long. | 1177 // types of transmissions. |
1191 if (write_blocked_ || send_alarm_->IsSet()) { | 1178 if (write_blocked_ || send_alarm_->IsSet()) { |
1192 return false; | 1179 return false; |
1193 } | 1180 } |
1194 | 1181 |
1195 QuicTime now = clock_->Now(); | 1182 QuicTime now = clock_->Now(); |
1196 QuicTime::Delta delay = congestion_manager_.TimeUntilSend( | 1183 QuicTime::Delta delay = congestion_manager_.TimeUntilSend( |
1197 now, transmission_type, retransmittable, handshake); | 1184 now, transmission_type, retransmittable, handshake); |
1198 if (delay.IsInfinite()) { | 1185 if (delay.IsInfinite()) { |
1199 return false; | 1186 return false; |
1200 } | 1187 } |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1311 // in the SerializedPacket to determine this for a packet. | 1298 // in the SerializedPacket to determine this for a packet. |
1312 IsHandshake handshake = level == ENCRYPTION_NONE ? IS_HANDSHAKE | 1299 IsHandshake handshake = level == ENCRYPTION_NONE ? IS_HANDSHAKE |
1313 : NOT_HANDSHAKE; | 1300 : NOT_HANDSHAKE; |
1314 | 1301 |
1315 // If we are not forced and we can't write, then simply return false; | 1302 // If we are not forced and we can't write, then simply return false; |
1316 if (forced == NO_FORCE && | 1303 if (forced == NO_FORCE && |
1317 !CanWrite(transmission_type, retransmittable, handshake)) { | 1304 !CanWrite(transmission_type, retransmittable, handshake)) { |
1318 return false; | 1305 return false; |
1319 } | 1306 } |
1320 | 1307 |
| 1308 // Some encryption algorithms require the packet sequence numbers not be |
| 1309 // repeated. |
| 1310 DCHECK_LE(sequence_number_of_last_inorder_packet_, sequence_number); |
| 1311 // Only increase this when packets have not been queued. Once they're queued |
| 1312 // due to a write block, there is the chance of sending forced and other |
| 1313 // higher priority packets out of order. |
| 1314 if (queued_packets_.empty()) { |
| 1315 sequence_number_of_last_inorder_packet_ = sequence_number; |
| 1316 } |
| 1317 |
1321 scoped_ptr<QuicEncryptedPacket> encrypted( | 1318 scoped_ptr<QuicEncryptedPacket> encrypted( |
1322 framer_.EncryptPacket(level, sequence_number, *packet)); | 1319 framer_.EncryptPacket(level, sequence_number, *packet)); |
1323 if (encrypted.get() == NULL) { | 1320 if (encrypted.get() == NULL) { |
1324 LOG(DFATAL) << ENDPOINT << "Failed to encrypt packet number " | 1321 LOG(DFATAL) << ENDPOINT << "Failed to encrypt packet number " |
1325 << sequence_number; | 1322 << sequence_number; |
1326 CloseConnection(QUIC_ENCRYPTION_FAILURE, false); | 1323 CloseConnection(QUIC_ENCRYPTION_FAILURE, false); |
1327 return false; | 1324 return false; |
1328 } | 1325 } |
1329 DLOG(INFO) << ENDPOINT << "Sending packet number " << sequence_number | 1326 DLOG(INFO) << ENDPOINT << "Sending packet number " << sequence_number |
1330 << " : " << (packet->is_fec_packet() ? "FEC " : | 1327 << " : " << (packet->is_fec_packet() ? "FEC " : |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1409 // WritePacketToWire returned -1, then |error| will be populated with | 1406 // WritePacketToWire returned -1, then |error| will be populated with |
1410 // an error code, which we want to pass along to the visitor. | 1407 // an error code, which we want to pass along to the visitor. |
1411 debug_visitor_->OnPacketSent(sequence_number, level, packet, | 1408 debug_visitor_->OnPacketSent(sequence_number, level, packet, |
1412 bytes_written == -1 ? *error : bytes_written); | 1409 bytes_written == -1 ? *error : bytes_written); |
1413 } | 1410 } |
1414 return bytes_written; | 1411 return bytes_written; |
1415 } | 1412 } |
1416 | 1413 |
1417 bool QuicConnection::OnSerializedPacket( | 1414 bool QuicConnection::OnSerializedPacket( |
1418 const SerializedPacket& serialized_packet) { | 1415 const SerializedPacket& serialized_packet) { |
1419 ack_notifier_manager_.OnSerializedPacket(serialized_packet); | |
1420 | |
1421 if (serialized_packet.retransmittable_frames) { | 1416 if (serialized_packet.retransmittable_frames) { |
1422 serialized_packet.retransmittable_frames-> | 1417 serialized_packet.retransmittable_frames-> |
1423 set_encryption_level(encryption_level_); | 1418 set_encryption_level(encryption_level_); |
1424 } | 1419 } |
1425 sent_packet_manager_.OnSerializedPacket(serialized_packet, | 1420 sent_packet_manager_.OnSerializedPacket(serialized_packet, |
1426 clock_->ApproximateNow()); | 1421 clock_->ApproximateNow()); |
1427 // The TransmissionType is NOT_RETRANSMISSION because all retransmissions | 1422 // The TransmissionType is NOT_RETRANSMISSION because all retransmissions |
1428 // serialize packets and invoke SendOrQueuePacket directly. | 1423 // serialize packets and invoke SendOrQueuePacket directly. |
1429 return SendOrQueuePacket(encryption_level_, | 1424 return SendOrQueuePacket(encryption_level_, |
1430 serialized_packet.sequence_number, | 1425 serialized_packet.sequence_number, |
(...skipping 432 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1863 // If we changed the generator's batch state, restore original batch state. | 1858 // If we changed the generator's batch state, restore original batch state. |
1864 if (!already_in_batch_mode_) { | 1859 if (!already_in_batch_mode_) { |
1865 DVLOG(1) << "Leaving Batch Mode."; | 1860 DVLOG(1) << "Leaving Batch Mode."; |
1866 connection_->packet_generator_.FinishBatchOperations(); | 1861 connection_->packet_generator_.FinishBatchOperations(); |
1867 } | 1862 } |
1868 DCHECK_EQ(already_in_batch_mode_, | 1863 DCHECK_EQ(already_in_batch_mode_, |
1869 connection_->packet_generator_.InBatchMode()); | 1864 connection_->packet_generator_.InBatchMode()); |
1870 } | 1865 } |
1871 | 1866 |
1872 } // namespace net | 1867 } // namespace net |
OLD | NEW |