diff options
author | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-05 19:43:06 +0000 |
---|---|---|
committer | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-05 19:43:06 +0000 |
commit | 3e60db87c3efc64a62a24e8dbd973ef8a83f420a (patch) | |
tree | 595f10254a4dcc4dcf36bd4ea3465a2b35db8a66 | |
parent | f0eda8165420562d6769b19f0126d5158dabd817 (diff) | |
download | chromium_src-3e60db87c3efc64a62a24e8dbd973ef8a83f420a.zip chromium_src-3e60db87c3efc64a62a24e8dbd973ef8a83f420a.tar.gz chromium_src-3e60db87c3efc64a62a24e8dbd973ef8a83f420a.tar.bz2 |
Land Recent QUIC changes.
Minor style change for initialization of hdr.msg_flags.
Merge internal change: 49828095
Enabled MaxNumStreams in quic/tools area.
Renamed MaxNumConnections to MaxNumStreams. Initialize
set_max_streams_per_connection only in MaxNumStreams, not for all tests.
Merge internal change: 49728455
QUIC - Adding a bit more information to a logged DFATAL.
Merge internal change: 49724887
Always expecting certificates for secure quic (test) clients. Changing
the end_to_end test to be insecure as it doesn't configure certs.
Merge internal change: 49723287
QUIC - Adding back a comment change that was deleted in the following CL.
https://chromiumcodereview.appspot.com/22020002/diff/1/net/quic/quic_connection.cc
Merge internal change: 49713074
Fail to process a header when the fec group offset is larger than the
packet sequence number.
Merge internal change: 49677837
Added a test to ensure that if the QuicPacketCreator has room for the
stream frame it can be added.
Merge internal change: 49620569
Adding support to QUIC for QUIC_VERSION_7, enabling lower overhead stream frames.
Merge internal change: 49540337
Rearrange code to match spdy_protocol.h SpdyFrameType declaration
order: mostly method declarations and definitions and case statements.
Merge internal change: 49503521
Refactor QuicReceivedEntropyManager and the other received packet code
into Quic ReceivedPacketManager to allow the entropy state and the ack's
received info to stay in sync.
Merge internal change: 49440136
Made a copy of QuicReceivedEntropyManager*.* code into QuicReceivedPacketManager*.*
Doing a better job of detecting unrecoverable compression state.
This is really tricky to get right.
We don't want to unilaterally close the connection if a stream closes before
headers are decompressed. If, for example, a server gets a protocol-layer
error while processing a request it could kill of a stream with something
like QUIC_MULTIPLE_TERMINATION_OFFSETS without sending the http/spdy
response. In this case, the client could continue processing on that
connection.
We basically want to kill off the connection if we get any packets for a
stream we've closed without processing complete headers. Which means
tracking state for every stream which closes without processing complete
headers which is a very unlikely OOM attack vector. I'm capping it at 20
somewhat arbitrarily, and calling it a day.
Merge internal change: 49373258
Removing logspam from quic connection.
Merge internal change: 49370853
Altering the server to not process headers until full headers have been read.
This fixes a problem where if we parsed partial headers, found an error, and
rejected the stream before getting full headers it would be possible to doom
the entire connection.
Merge internal change: 49239328
R=rch@chromium.org
Review URL: https://chromiumcodereview.appspot.com/22122002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@215661 0039d316-1c4b-4281-b951-d872f2087c98
48 files changed, 1969 insertions, 806 deletions
diff --git a/net/net.gyp b/net/net.gyp index a973e08..70436d1 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -815,8 +815,8 @@ 'quic/quic_packet_generator.h', 'quic/quic_protocol.cc', 'quic/quic_protocol.h', - 'quic/quic_received_entropy_manager.cc', - 'quic/quic_received_entropy_manager.h', + 'quic/quic_received_packet_manager.cc', + 'quic/quic_received_packet_manager.h', 'quic/quic_reliable_client_stream.cc', 'quic/quic_reliable_client_stream.h', 'quic/quic_sent_entropy_manager.cc', @@ -1714,6 +1714,8 @@ 'quic/test_tools/quic_framer_peer.h', 'quic/test_tools/quic_packet_creator_peer.cc', 'quic/test_tools/quic_packet_creator_peer.h', + 'quic/test_tools/quic_received_packet_manager_peer.cc', + 'quic/test_tools/quic_received_packet_manager_peer.h', 'quic/test_tools/quic_session_peer.cc', 'quic/test_tools/quic_session_peer.h', 'quic/test_tools/quic_test_utils.cc', @@ -1741,7 +1743,7 @@ 'quic/quic_packet_creator_test.cc', 'quic/quic_packet_generator_test.cc', 'quic/quic_protocol_test.cc', - 'quic/quic_received_entropy_manager_test.cc', + 'quic/quic_received_packet_manager_test.cc', 'quic/quic_reliable_client_stream_test.cc', 'quic/quic_sent_entropy_manager_test.cc', 'quic/quic_session_test.cc', diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc index 4badfa7..ea738af 100644 --- a/net/quic/quic_connection.cc +++ b/net/quic/quic_connection.cc @@ -81,9 +81,6 @@ QuicConnection::QuicConnection(QuicGuid guid, guid_(guid), peer_address_(address), largest_seen_packet_with_ack_(0), - peer_largest_observed_packet_(0), - least_packet_awaited_by_peer_(1), - peer_least_packet_awaiting_ack_(0), handling_retransmission_timeout_(false), write_blocked_(false), debug_visitor_(NULL), @@ -95,7 +92,6 @@ QuicConnection::QuicConnection(QuicGuid guid, creation_time_(clock_->ApproximateNow()), time_of_last_received_packet_(clock_->ApproximateNow()), time_of_last_sent_packet_(clock_->ApproximateNow()), - time_largest_observed_(QuicTime::Zero()), congestion_manager_(clock_, kTCP), version_negotiation_state_(START_NEGOTIATION), max_packets_per_retransmission_alarm_(kMaxPacketsPerRetransmissionAlarm), @@ -107,11 +103,7 @@ QuicConnection::QuicConnection(QuicGuid guid, helper_->SetConnection(this); helper_->SetTimeoutAlarm(idle_network_timeout_); framer_.set_visitor(this); - framer_.set_received_entropy_calculator(&received_entropy_manager_); - outgoing_ack_.sent_info.least_unacked = 0; - outgoing_ack_.sent_info.entropy_hash = 0; - outgoing_ack_.received_info.largest_observed = 0; - outgoing_ack_.received_info.entropy_hash = 0; + framer_.set_received_entropy_calculator(&received_packet_manager_); /* if (FLAGS_fake_packet_loss_percentage > 0) { @@ -130,7 +122,6 @@ QuicConnection::~QuicConnection() { it != queued_packets_.end(); ++it) { delete it->packet; } - DLOG(INFO) << ENDPOINT << "write_blocked: " << write_blocked_; } bool QuicConnection::SelectMutualVersion( @@ -290,8 +281,8 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { // If this packet has already been seen, or that the sender // has told us will not be retransmitted, then stop processing the packet. - if (!IsAwaitingPacket(outgoing_ack_.received_info, - header.packet_sequence_number)) { + if (!received_packet_manager_.IsAwaitingPacket( + header.packet_sequence_number)) { return false; } @@ -366,8 +357,20 @@ bool QuicConnection::OnAckFrame(const QuicAckFrame& incoming_ack) { incoming_ack.received_info.missing_packets.size() >= QuicFramer::GetMaxUnackedPackets(last_header_); - UpdatePacketInformationReceivedByPeer(incoming_ack); - UpdatePacketInformationSentByPeer(incoming_ack); + received_packet_manager_.UpdatePacketInformationReceivedByPeer(incoming_ack); + received_packet_manager_.UpdatePacketInformationSentByPeer(incoming_ack); + // Possibly close any FecGroups which are now irrelevant. + CloseFecGroupsBefore(incoming_ack.sent_info.least_unacked + 1); + + sent_entropy_manager_.ClearEntropyBefore( + received_packet_manager_.least_packet_awaited_by_peer() - 1); + + SequenceNumberSet acked_packets; + HandleAckForSentPackets(incoming_ack, &acked_packets); + HandleAckForSentFecPackets(incoming_ack, &acked_packets); + if (acked_packets.size() > 0) { + visitor_->OnAck(acked_packets); + } congestion_manager_.OnIncomingAckFrame(incoming_ack, time_of_last_received_packet_); @@ -407,10 +410,10 @@ bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) { } if (incoming_ack.received_info.largest_observed < - peer_largest_observed_packet_) { + received_packet_manager_.peer_largest_observed_packet()) { DLOG(ERROR) << ENDPOINT << "Peer's largest_observed packet decreased:" << incoming_ack.received_info.largest_observed << " vs " - << peer_largest_observed_packet_; + << received_packet_manager_.peer_largest_observed_packet(); // A new ack has a diminished largest_observed value. Error out. // If this was an old packet, we wouldn't even have checked. return false; @@ -421,10 +424,11 @@ bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) { DCHECK_LE(incoming_ack.received_info.missing_packets.size(), QuicFramer::GetMaxUnackedPackets(last_header_)); - if (incoming_ack.sent_info.least_unacked < peer_least_packet_awaiting_ack_) { + if (incoming_ack.sent_info.least_unacked < + received_packet_manager_.peer_least_packet_awaiting_ack()) { DLOG(ERROR) << ENDPOINT << "Peer's sent low least_unacked: " - << incoming_ack.sent_info.least_unacked - << " vs " << peer_least_packet_awaiting_ack_; + << incoming_ack.sent_info.least_unacked << " vs " + << received_packet_manager_.peer_least_packet_awaiting_ack(); // We never process old ack frames, so this number should only increase. return false; } @@ -450,11 +454,11 @@ bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) { if (!incoming_ack.received_info.missing_packets.empty() && *incoming_ack.received_info.missing_packets.begin() < - least_packet_awaited_by_peer_) { + received_packet_manager_.least_packet_awaited_by_peer()) { DLOG(ERROR) << ENDPOINT << "Peer sent missing packet: " << *incoming_ack.received_info.missing_packets.begin() << "smaller than least_packet_awaited_by_peer_: " - << least_packet_awaited_by_peer_; + << received_packet_manager_.least_packet_awaited_by_peer(); return false; } @@ -477,7 +481,8 @@ void QuicConnection::HandleAckForSentPackets(const QuicAckFrame& incoming_ack, UnackedPacketMap::iterator it = unacked_packets_.begin(); while (it != unacked_packets_.end()) { QuicPacketSequenceNumber sequence_number = it->first; - if (sequence_number > peer_largest_observed_packet_) { + if (sequence_number > + received_packet_manager_.peer_largest_observed_packet()) { // These are very new sequence_numbers. break; } @@ -519,7 +524,8 @@ void QuicConnection::HandleAckForSentFecPackets( UnackedPacketMap::iterator it = unacked_fec_packets_.begin(); while (it != unacked_fec_packets_.end()) { QuicPacketSequenceNumber sequence_number = it->first; - if (sequence_number > peer_largest_observed_packet_) { + if (sequence_number > + received_packet_manager_.peer_largest_observed_packet()) { break; } if (!IsAwaitingPacket(incoming_ack.received_info, sequence_number)) { @@ -534,68 +540,6 @@ void QuicConnection::HandleAckForSentFecPackets( } } -void QuicConnection::UpdatePacketInformationReceivedByPeer( - const QuicAckFrame& incoming_ack) { - // ValidateAck should fail if largest_observed ever shrinks. - DCHECK_LE(peer_largest_observed_packet_, - incoming_ack.received_info.largest_observed); - peer_largest_observed_packet_ = incoming_ack.received_info.largest_observed; - - if (incoming_ack.received_info.missing_packets.empty()) { - least_packet_awaited_by_peer_ = peer_largest_observed_packet_ + 1; - } else { - least_packet_awaited_by_peer_ = - *(incoming_ack.received_info.missing_packets.begin()); - } - - sent_entropy_manager_.ClearEntropyBefore(least_packet_awaited_by_peer_ - 1); - - SequenceNumberSet acked_packets; - HandleAckForSentPackets(incoming_ack, &acked_packets); - HandleAckForSentFecPackets(incoming_ack, &acked_packets); - - if (acked_packets.size() > 0) { - visitor_->OnAck(acked_packets); - } -} - -bool QuicConnection::DontWaitForPacketsBefore( - QuicPacketSequenceNumber least_unacked) { - size_t missing_packets_count = - outgoing_ack_.received_info.missing_packets.size(); - outgoing_ack_.received_info.missing_packets.erase( - outgoing_ack_.received_info.missing_packets.begin(), - outgoing_ack_.received_info.missing_packets.lower_bound(least_unacked)); - return missing_packets_count != - outgoing_ack_.received_info.missing_packets.size(); -} - -void QuicConnection::UpdatePacketInformationSentByPeer( - const QuicAckFrame& incoming_ack) { - // ValidateAck() should fail if peer_least_packet_awaiting_ack_ shrinks. - DCHECK_LE(peer_least_packet_awaiting_ack_, - incoming_ack.sent_info.least_unacked); - if (incoming_ack.sent_info.least_unacked > peer_least_packet_awaiting_ack_) { - bool missed_packets = - DontWaitForPacketsBefore(incoming_ack.sent_info.least_unacked); - if (missed_packets || incoming_ack.sent_info.least_unacked > - outgoing_ack_.received_info.largest_observed + 1) { - DVLOG(1) << ENDPOINT << "Updating entropy hashed since we missed packets"; - // There were some missing packets that we won't ever get now. Recalculate - // the received entropy hash. - received_entropy_manager_.RecalculateEntropyHash( - incoming_ack.sent_info.least_unacked, - incoming_ack.sent_info.entropy_hash); - } - peer_least_packet_awaiting_ack_ = incoming_ack.sent_info.least_unacked; - } - DCHECK(outgoing_ack_.received_info.missing_packets.empty() || - *outgoing_ack_.received_info.missing_packets.begin() >= - peer_least_packet_awaiting_ack_); - // Possibly close any FecGroups which are now irrelevant - CloseFecGroupsBefore(incoming_ack.sent_info.least_unacked + 1); -} - void QuicConnection::OnFecData(const QuicFecData& fec) { DCHECK_EQ(IN_FEC_GROUP, last_header_.is_in_fec_group); DCHECK_NE(0u, last_header_.fec_group); @@ -662,7 +606,8 @@ void QuicConnection::OnPacketComplete() { if ((last_stream_frames_.empty() || visitor_->OnPacket(self_address_, peer_address_, last_header_, last_stream_frames_))) { - RecordPacketReceived(last_header_); + received_packet_manager_.RecordPacketReceived( + last_header_, time_of_last_received_packet_); } MaybeSendAckInResponseToPacket(); @@ -670,19 +615,12 @@ void QuicConnection::OnPacketComplete() { } QuicAckFrame* QuicConnection::CreateAckFrame() { - UpdateOutgoingAck(); - if (time_largest_observed_ == QuicTime::Zero()) { - // We have not received any new higher sequence numbers since we sent our - // last ACK. - outgoing_ack_.received_info.delta_time_largest_observed = - QuicTime::Delta::Infinite(); - } else { - outgoing_ack_.received_info.delta_time_largest_observed = - clock_->ApproximateNow().Subtract(time_largest_observed_); - - time_largest_observed_ = QuicTime::Zero(); - } - return new QuicAckFrame(outgoing_ack_); + QuicAckFrame* outgoing_ack = new QuicAckFrame(); + received_packet_manager_.UpdateReceivedPacketInfo( + &(outgoing_ack->received_info), clock_->ApproximateNow()); + UpdateSentPacketInfo(&(outgoing_ack->sent_info)); + DVLOG(1) << ENDPOINT << "Creating ack frame: " << *outgoing_ack; + return outgoing_ack; } QuicCongestionFeedbackFrame* QuicConnection::CreateFeedbackFrame() { @@ -859,34 +797,6 @@ bool QuicConnection::WriteQueuedPackets() { return !write_blocked_; } -void QuicConnection::RecordPacketReceived(const QuicPacketHeader& header) { - QuicPacketSequenceNumber sequence_number = header.packet_sequence_number; - DCHECK(IsAwaitingPacket(outgoing_ack_.received_info, sequence_number)); - - InsertMissingPacketsBetween( - &outgoing_ack_.received_info, - max(outgoing_ack_.received_info.largest_observed + 1, - peer_least_packet_awaiting_ack_), - header.packet_sequence_number); - - if (outgoing_ack_.received_info.largest_observed > - header.packet_sequence_number) { - // We've gotten one of the out of order packets - remove it from our - // "missing packets" list. - DVLOG(1) << ENDPOINT << "Removing " << sequence_number - << " from missing list"; - outgoing_ack_.received_info.missing_packets.erase(sequence_number); - } - if (header.packet_sequence_number > - outgoing_ack_.received_info.largest_observed) { - outgoing_ack_.received_info.largest_observed = - header.packet_sequence_number; - time_largest_observed_ = time_of_last_received_packet_; - } - received_entropy_manager_.RecordPacketEntropyHash( - sequence_number, header.entropy_hash); -} - bool QuicConnection::MaybeRetransmitPacketForRTO( QuicPacketSequenceNumber sequence_number) { DCHECK_EQ(ContainsKey(unacked_packets_, sequence_number), @@ -906,8 +816,8 @@ bool QuicConnection::MaybeRetransmitPacketForRTO( // any RTO for packets larger than the peer's largest observed packet; it may // have been received by the peer and just wasn't acked due to the ack frame // running out of space. - if (received_truncated_ack_ && - sequence_number > peer_largest_observed_packet_ && + if (received_truncated_ack_ && sequence_number > + received_packet_manager_.peer_largest_observed_packet() && // We allow retransmission of already retransmitted packets so that we // retransmit packets that were retransmissions of the packet with // sequence number < the largest observed field of the truncated ack. @@ -1128,7 +1038,7 @@ bool QuicConnection::WritePacket(EncryptionLevel level, DCHECK(encrypted->length() <= kMaxPacketSize) << "Packet " << sequence_number << " will not be read; too large: " << packet->length() << " " << encrypted->length() << " " - << outgoing_ack_ << " forced: " << (forced == FORCE ? "yes" : "no"); + << " forced: " << (forced == FORCE ? "yes" : "no"); int error; QuicTime now = clock_->Now(); @@ -1245,27 +1155,21 @@ bool QuicConnection::ShouldSimulateLostPacket() { */ } -void QuicConnection::UpdateOutgoingAck() { +void QuicConnection::UpdateSentPacketInfo(SentPacketInfo* sent_info) { if (!unacked_packets_.empty()) { - outgoing_ack_.sent_info.least_unacked = unacked_packets_.begin()->first; + sent_info->least_unacked = unacked_packets_.begin()->first; } else { // If there are no unacked packets, set the least unacked packet to // sequence_number() + 1 since that will be the sequence number of this // ack packet whenever it is sent. - outgoing_ack_.sent_info.least_unacked = - packet_creator_.sequence_number() + 1; + sent_info->least_unacked = packet_creator_.sequence_number() + 1; } - outgoing_ack_.sent_info.entropy_hash = sent_entropy_manager_.EntropyHash( - outgoing_ack_.sent_info.least_unacked - 1); - outgoing_ack_.received_info.entropy_hash = - received_entropy_manager_.EntropyHash( - outgoing_ack_.received_info.largest_observed); + sent_info->entropy_hash = sent_entropy_manager_.EntropyHash( + sent_info->least_unacked - 1); } void QuicConnection::SendAck() { helper_->ClearAckAlarm(); - UpdateOutgoingAck(); - DVLOG(1) << ENDPOINT << "Sending ack: " << outgoing_ack_; // TODO(rch): delay this until the CreateFeedbackFrame // method is invoked. This requires changes SetShouldSendAck @@ -1464,8 +1368,9 @@ void QuicConnection::SendConnectionClosePacket(QuicErrorCode error, QuicConnectionCloseFrame frame; frame.error_code = error; frame.error_details = details; - UpdateOutgoingAck(); - frame.ack_frame = outgoing_ack_; + UpdateSentPacketInfo(&frame.ack_frame.sent_info); + received_packet_manager_.UpdateReceivedPacketInfo( + &frame.ack_frame.received_info, clock_->ApproximateNow()); SerializedPacket serialized_packet = packet_creator_.SerializeConnectionClose(&frame); diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h index 8f4f3f1..4ac4fa4 100644 --- a/net/quic/quic_connection.h +++ b/net/quic/quic_connection.h @@ -32,7 +32,7 @@ #include "net/quic/quic_packet_creator.h" #include "net/quic/quic_packet_generator.h" #include "net/quic/quic_protocol.h" -#include "net/quic/quic_received_entropy_manager.h" +#include "net/quic/quic_received_packet_manager.h" #include "net/quic/quic_sent_entropy_manager.h" #include "net/quic/quic_stats.h" @@ -329,9 +329,6 @@ class NET_EXPORT_PRIVATE QuicConnection const QuicClock* clock() const { return clock_; } QuicRandom* random_generator() const { return random_generator_; } - // Updates the internal state concerning which packets have been acked. - void RecordPacketReceived(const QuicPacketHeader& header); - // Called by a RetransmissionAlarm when the timer goes off. If the peer // appears to be sending truncated acks, this returns false to indicate // failure, otherwise it calls MaybeRetransmitPacket and returns true. @@ -412,12 +409,6 @@ class NET_EXPORT_PRIVATE QuicConnection const QuicDecrypter* alternative_decrypter() const; protected: - // Deletes all missing packets before least unacked. The connection won't - // process any packets with sequence number before |least_unacked| that it - // received after this call. Returns true if there were missing packets before - // |least_unacked| unacked, false otherwise. - bool DontWaitForPacketsBefore(QuicPacketSequenceNumber least_unacked); - // Send a packet to the peer using encryption |level|. If |sequence_number| // is present in the |retransmission_map_|, then contents of this packet will // be retransmitted with a new sequence number if it's not acked by the peer. @@ -569,15 +560,8 @@ class NET_EXPORT_PRIVATE QuicConnection void HandleAckForSentFecPackets(const QuicAckFrame& incoming_ack, SequenceNumberSet* acked_packets); - // These two are called by OnAckFrame. - // - // Updates internal state based on incoming_ack.received_info - void UpdatePacketInformationReceivedByPeer( - const QuicAckFrame& incoming_ack); - // Updates internal state based on incoming_ack.sent_info - void UpdatePacketInformationSentByPeer(const QuicAckFrame& incoming_ack); - - void UpdateOutgoingAck(); + // Update the |sent_info| for an outgoing ack. + void UpdateSentPacketInfo(SentPacketInfo* sent_info); void MaybeSendAckInResponseToPacket(); @@ -606,20 +590,11 @@ class NET_EXPORT_PRIVATE QuicConnection QuicPacketHeader last_header_; std::vector<QuicStreamFrame> last_stream_frames_; - QuicAckFrame outgoing_ack_; QuicCongestionFeedbackFrame outgoing_congestion_feedback_; // Track some peer state so we can do less bookkeeping // Largest sequence sent by the peer which had an ack frame (latest ack info). QuicPacketSequenceNumber largest_seen_packet_with_ack_; - // Largest sequence number that the peer has observed. Mostly received, - // missing in case of truncated acks. - QuicPacketSequenceNumber peer_largest_observed_packet_; - // Least sequence number which the peer is still waiting for. - QuicPacketSequenceNumber least_packet_awaited_by_peer_; - // Least sequence number of the the packet sent by the peer for which it - // hasn't received an ack. - QuicPacketSequenceNumber peer_least_packet_awaiting_ack_; // When new packets are created which may be retransmitted, they are added // to this map, which contains owning pointers to the contained frames. @@ -662,7 +637,7 @@ class NET_EXPORT_PRIVATE QuicConnection FecGroupMap group_map_; - QuicReceivedEntropyManager received_entropy_manager_; + QuicReceivedPacketManager received_packet_manager_; QuicSentEntropyManager sent_entropy_manager_; QuicConnectionVisitorInterface* visitor_; @@ -681,15 +656,12 @@ class NET_EXPORT_PRIVATE QuicConnection QuicConnectionStats stats_; // The time that we got a packet for this connection. + // This is used for timeouts, and does not indicate the packet was processed. QuicTime time_of_last_received_packet_; // The time that we last sent a packet for this connection. QuicTime time_of_last_sent_packet_; - // Member holding the time we received the largest_observed sequence number. - // Needed for calculating delta_time_largest_observed. - QuicTime time_largest_observed_; - // Congestion manager which controls the rate the connection sends packets // as well as collecting and generating congestion feedback. QuicCongestionManager congestion_manager_; diff --git a/net/quic/quic_connection_helper_test.cc b/net/quic/quic_connection_helper_test.cc index a955e4c5..6e84c0f 100644 --- a/net/quic/quic_connection_helper_test.cc +++ b/net/quic/quic_connection_helper_test.cc @@ -126,7 +126,7 @@ class QuicConnectionHelperTest : public ::testing::Test { QuicFrames frames; frames.push_back(QuicFrame(&frame_)); - return framer_.ConstructFrameDataPacket(header_, frames).packet; + return framer_.BuildUnsizedDataPacket(header_, frames).packet; } // Returns a newly created packet to send ack data. @@ -147,7 +147,7 @@ class QuicConnectionHelperTest : public ::testing::Test { frames.push_back(QuicFrame(&ack)); frames.push_back(QuicFrame(&feedback)); scoped_ptr<QuicPacket> packet( - framer_.ConstructFrameDataPacket(header_, frames).packet); + framer_.BuildUnsizedDataPacket(header_, frames).packet); return framer_.EncryptPacket( ENCRYPTION_NONE, header_.packet_sequence_number, *packet); } @@ -196,7 +196,7 @@ class QuicConnectionHelperTest : public ::testing::Test { QuicFrames frames; frames.push_back(frame); scoped_ptr<QuicPacket> packet( - framer_.ConstructFrameDataPacket(header_, frames).packet); + framer_.BuildUnsizedDataPacket(header_, frames).packet); return framer_.EncryptPacket( ENCRYPTION_NONE, header_.packet_sequence_number, *packet); } diff --git a/net/quic/quic_connection_test.cc b/net/quic/quic_connection_test.cc index 2161d53a..80854aa 100644 --- a/net/quic/quic_connection_test.cc +++ b/net/quic/quic_connection_test.cc @@ -428,7 +428,6 @@ class TestConnection : public QuicConnection { } using QuicConnection::SendOrQueuePacket; - using QuicConnection::DontWaitForPacketsBefore; using QuicConnection::SelectMutualVersion; private: @@ -464,7 +463,8 @@ class QuicConnectionTest : public ::testing::Test { } QuicAckFrame* outgoing_ack() { - return QuicConnectionPeer::GetOutgoingAck(&connection_); + outgoing_ack_.reset(QuicConnectionPeer::CreateAckFrame(&connection_)); + return outgoing_ack_.get(); } QuicAckFrame* last_ack() { @@ -601,7 +601,7 @@ class QuicConnectionTest : public ::testing::Test { } fec_data.redundancy = data_packet->FecProtectedData(); scoped_ptr<QuicPacket> fec_packet( - framer_.ConstructFecPacket(header_, fec_data).packet); + framer_.BuildFecPacket(header_, fec_data).packet); scoped_ptr<QuicEncryptedPacket> encrypted( framer_.EncryptPacket(ENCRYPTION_NONE, number, *fec_packet)); @@ -662,7 +662,7 @@ class QuicConnectionTest : public ::testing::Test { QuicFrame frame(&frame1_); frames.push_back(frame); QuicPacket* packet = - framer_.ConstructFrameDataPacket(header_, frames).packet; + framer_.BuildUnsizedDataPacket(header_, frames).packet; EXPECT_TRUE(packet != NULL); return packet; } @@ -686,7 +686,7 @@ class QuicConnectionTest : public ::testing::Test { QuicFrame frame(&qccf); frames.push_back(frame); QuicPacket* packet = - framer_.ConstructFrameDataPacket(header_, frames).packet; + framer_.BuildUnsizedDataPacket(header_, frames).packet; EXPECT_TRUE(packet != NULL); return packet; } @@ -712,6 +712,7 @@ class QuicConnectionTest : public ::testing::Test { QuicPacketHeader revived_header_; QuicStreamFrame frame1_; QuicStreamFrame frame2_; + scoped_ptr<QuicAckFrame> outgoing_ack_; bool accept_packet_; private: @@ -939,13 +940,6 @@ TEST_F(QuicConnectionTest, AckAll) { ProcessAckPacket(&frame1, true); } -TEST_F(QuicConnectionTest, DontWaitForPacketsBefore) { - ProcessPacket(2); - ProcessPacket(7); - EXPECT_TRUE(connection_.DontWaitForPacketsBefore(4)); - EXPECT_EQ(3u, outgoing_ack()->received_info.missing_packets.size()); -} - TEST_F(QuicConnectionTest, BasicSending) { EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(6); QuicPacketSequenceNumber last_packet; @@ -1006,15 +1000,15 @@ TEST_F(QuicConnectionTest, FECSending) { // All packets carry version info till version is negotiated. size_t payload_length; connection_.options()->max_packet_length = - GetPacketLengthForOneStream( - kIncludeVersion, IN_FEC_GROUP, &payload_length); + GetPacketLengthForOneStream(connection_.version(), kIncludeVersion, + IN_FEC_GROUP, &payload_length); // And send FEC every two packets. connection_.options()->max_packets_per_fec_group = 2; // Send 4 data packets and 2 FEC packets. EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(6); - // TODO(ianswett): The first stream frame will consume 2 fewer bytes. - const string payload(payload_length * 4, 'a'); + // The first stream frame will consume 2 fewer bytes than the other three. + const string payload(payload_length * 4 - 6, 'a'); connection_.SendStreamData(1, payload, 0, !kFin); // Expect the FEC group to be closed after SendStreamData. EXPECT_FALSE(creator_.ShouldSendFec(true)); @@ -1024,8 +1018,8 @@ TEST_F(QuicConnectionTest, FECQueueing) { // All packets carry version info till version is negotiated. size_t payload_length; connection_.options()->max_packet_length = - GetPacketLengthForOneStream( - kIncludeVersion, IN_FEC_GROUP, &payload_length); + GetPacketLengthForOneStream(connection_.version(), kIncludeVersion, + IN_FEC_GROUP, &payload_length); // And send FEC every two packets. connection_.options()->max_packets_per_fec_group = 2; @@ -1947,8 +1941,8 @@ TEST_F(QuicConnectionTest, TestQueueLimitsOnSendStreamData) { // All packets carry version info till version is negotiated. size_t payload_length; connection_.options()->max_packet_length = - GetPacketLengthForOneStream( - kIncludeVersion, NOT_IN_FEC_GROUP, &payload_length); + GetPacketLengthForOneStream(connection_.version(), kIncludeVersion, + NOT_IN_FEC_GROUP, &payload_length); // Queue the first packet. EXPECT_CALL(*send_algorithm_, @@ -1964,13 +1958,13 @@ TEST_F(QuicConnectionTest, LoopThroughSendingPackets) { // All packets carry version info till version is negotiated. size_t payload_length; connection_.options()->max_packet_length = - GetPacketLengthForOneStream( - kIncludeVersion, NOT_IN_FEC_GROUP, &payload_length); + GetPacketLengthForOneStream(connection_.version(), kIncludeVersion, + NOT_IN_FEC_GROUP, &payload_length); // Queue the first packet. EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(7); - // TODO(ianswett): The first stream frame will consume 2 fewer bytes. - const string payload(payload_length * 7, 'a'); + // The first stream frame will consume 2 fewer bytes than the other six. + const string payload(payload_length * 7 - 12, 'a'); EXPECT_EQ(payload.size(), connection_.SendStreamData(1, payload, 0, !kFin).bytes_consumed); } @@ -2001,7 +1995,7 @@ TEST_F(QuicConnectionTest, PublicReset) { header.public_header.version_flag = false; header.rejected_sequence_number = 10101; scoped_ptr<QuicEncryptedPacket> packet( - framer_.ConstructPublicResetPacket(header)); + framer_.BuildPublicResetPacket(header)); EXPECT_CALL(visitor_, ConnectionClose(QUIC_PUBLIC_RESET, true)); connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *packet); } @@ -2144,7 +2138,7 @@ TEST_F(QuicConnectionTest, SendVersionNegotiationPacket) { QuicFrame frame(&frame1_); frames.push_back(frame); scoped_ptr<QuicPacket> packet( - framer_.ConstructFrameDataPacket(header, frames).packet); + framer_.BuildUnsizedDataPacket(header, frames).packet); scoped_ptr<QuicEncryptedPacket> encrypted( framer_.EncryptPacket(ENCRYPTION_NONE, 12, *packet)); @@ -2281,7 +2275,7 @@ TEST_F(QuicConnectionTest, DontProcessFramesIfPacketClosedConnection) { frames.push_back(stream_frame); frames.push_back(close_frame); scoped_ptr<QuicPacket> packet( - framer_.ConstructFrameDataPacket(header_, frames).packet); + framer_.BuildUnsizedDataPacket(header_, frames).packet); EXPECT_TRUE(NULL != packet.get()); scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket( ENCRYPTION_NONE, 1, *packet)); @@ -2293,26 +2287,32 @@ TEST_F(QuicConnectionTest, DontProcessFramesIfPacketClosedConnection) { connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); } -//// The QUIC_VERSION_X versions are deliberately set, rather than using all -//// values in kSupportedQuicVersions. -//TEST_F(QuicConnectionTest, SelectMutualVersion) { -// // Set the connection to speak QUIC_VERSION_6. -// connection_.set_version(QUIC_VERSION_6); -// EXPECT_EQ(connection_.version(), QUIC_VERSION_6); -// -// // Pass in available versions which includes a higher mutually supported -// // version. The higher mutually supported version should be selected. -// EXPECT_TRUE( -// connection_.SelectMutualVersion({QUIC_VERSION_6, QUIC_VERSION_7})); -// EXPECT_EQ(connection_.version(), QUIC_VERSION_7); -// -// // Expect that the lower version is selected. -// EXPECT_TRUE(connection_.SelectMutualVersion({QUIC_VERSION_6})); -// EXPECT_EQ(connection_.version(), QUIC_VERSION_6); -// -// // Shouldn't be able to find a mutually supported version. -// EXPECT_FALSE(connection_.SelectMutualVersion({QUIC_VERSION_UNSUPPORTED})); -//} +// The QUIC_VERSION_X versions are deliberately set, rather than using all +// values in kSupportedQuicVersions. +TEST_F(QuicConnectionTest, SelectMutualVersion) { + // Set the connection to speak QUIC_VERSION_6. + connection_.set_version(QUIC_VERSION_6); + EXPECT_EQ(connection_.version(), QUIC_VERSION_6); + + // Pass in available versions which includes a higher mutually supported + // version. The higher mutually supported version should be selected. + QuicVersionVector available_versions; + available_versions.push_back(QUIC_VERSION_6); + available_versions.push_back(QUIC_VERSION_7); + EXPECT_TRUE(connection_.SelectMutualVersion(available_versions)); + EXPECT_EQ(connection_.version(), QUIC_VERSION_7); + + // Expect that the lower version is selected. + QuicVersionVector lower_version; + lower_version.push_back(QUIC_VERSION_6); + EXPECT_TRUE(connection_.SelectMutualVersion(lower_version)); + EXPECT_EQ(connection_.version(), QUIC_VERSION_6); + + // Shouldn't be able to find a mutually supported version. + QuicVersionVector unsupported_version; + unsupported_version.push_back(QUIC_VERSION_UNSUPPORTED); + EXPECT_FALSE(connection_.SelectMutualVersion(unsupported_version)); +} TEST_F(QuicConnectionTest, ConnectionCloseWhenNotWriteBlocked) { helper_->set_blocked(false); // Already default. diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc index e4484f6..e1f8ec8 100644 --- a/net/quic/quic_framer.cc +++ b/net/quic/quic_framer.cc @@ -33,6 +33,32 @@ const QuicPacketSequenceNumber k1ByteSequenceNumberMask = const QuicGuid k1ByteGuidMask = GG_UINT64_C(0x00000000000000FF); const QuicGuid k4ByteGuidMask = GG_UINT64_C(0x00000000FFFFFFFF); +// Mask to determine if it's a special frame type(Stream, Ack, or +// Congestion Control) by checking if the first bit is 0, then shifting right. +const uint8 kQuicFrameType0BitMask = 0x01; + +// Default frame type shift and mask. +const uint8 kQuicDefaultFrameTypeShift = 3; +const uint8 kQuicDefaultFrameTypeMask = 0x07; + +// Stream frame relative shifts and masks for interpreting the stream flags. +// StreamID may be 1, 2, 3, or 4 bytes. +const uint8 kQuicStreamIdShift = 2; +const uint8 kQuicStreamIDLengthMask = 0x03; + +// Offset may be 0, 2, 3, 4, 5, 6, 7, 8 bytes. +const uint8 kQuicStreamOffsetShift = 3; +const uint8 kQuicStreamOffsetMask = 0x07; + +// Data length may be 0 or 2 bytes. +const uint8 kQuicStreamDataLengthShift = 1; +const uint8 kQuicStreamDataLengthMask = 0x01; + +// Fin bit may be set or not. +const uint8 kQuicStreamFinShift = 1; +const uint8 kQuicStreamFinMask = 0x01; + + const uint32 kInvalidDeltaTime = 0xffffffff; // Returns the absolute value of the difference between |a| and |b|. @@ -72,28 +98,19 @@ QuicFramer::QuicFramer(QuicVersion version, QuicFramer::~QuicFramer() {} -bool CanTruncate(const QuicFrame& frame) { - if (frame.type == ACK_FRAME || - frame.type == CONNECTION_CLOSE_FRAME) { - return true; - } - return false; -} - -// static -size_t QuicFramer::GetMinStreamFrameSize() { - return kQuicFrameTypeSize + kQuicStreamIdSize + - kQuicStreamFinSize + kQuicStreamOffsetSize + kQuicStreamPayloadLengthSize; -} - // static -size_t QuicFramer::GetMinStreamFrameSize(QuicStreamId stream_id, +size_t QuicFramer::GetMinStreamFrameSize(QuicVersion version, + QuicStreamId stream_id, QuicStreamOffset offset, - bool last_frame) { - // TODO(ianswett): Remove kQuicStreamFinSize for the next STREAM framing. + bool last_frame_in_packet) { + if (version == QUIC_VERSION_6) { + return kQuicFrameTypeSize + kQuicMaxStreamIdSize + + kQuicStreamFinSize + kQuicMaxStreamOffsetSize + + kQuicStreamPayloadLengthSize; + } return kQuicFrameTypeSize + GetStreamIdSize(stream_id) + - GetStreamOffsetSize(offset) + kQuicStreamFinSize + - kQuicStreamPayloadLengthSize; + GetStreamOffsetSize(offset) + + (last_frame_in_packet ? 0 : kQuicStreamPayloadLengthSize); } // static @@ -106,7 +123,7 @@ size_t QuicFramer::GetMinAckFrameSize() { // static size_t QuicFramer::GetMinRstStreamFrameSize() { - return kQuicFrameTypeSize + kQuicStreamIdSize + kQuicErrorCodeSize + + return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicErrorCodeSize + kQuicErrorDetailsLengthSize; } @@ -119,7 +136,7 @@ size_t QuicFramer::GetMinConnectionCloseFrameSize() { // static size_t QuicFramer::GetMinGoAwayFrameSize() { return kQuicFrameTypeSize + kQuicErrorCodeSize + kQuicErrorDetailsLengthSize + - kQuicStreamIdSize; + kQuicMaxStreamIdSize; } // static @@ -134,14 +151,53 @@ size_t QuicFramer::GetMaxUnackedPackets(QuicPacketHeader header) { // static size_t QuicFramer::GetStreamIdSize(QuicStreamId stream_id) { + // Sizes are 1 through 4 bytes. + for (int i = 1; i <= 4; ++i) { + stream_id >>= 8; + if (stream_id == 0) { + return i; + } + } + LOG(DFATAL) << "Failed to determine StreamIDSize."; return 4; } // static size_t QuicFramer::GetStreamOffsetSize(QuicStreamOffset offset) { + // 0 is a special case. + if (offset == 0) { + return 0; + } + // 2 through 8 are the remaining sizes. + offset >>= 8; + for (int i = 2; i <= 8; ++i) { + offset >>= 8; + if (offset == 0) { + return i; + } + } + LOG(DFATAL) << "Failed to determine StreamOffsetSize."; return 8; } +// static +size_t QuicFramer::GetVersionNegotiationPacketSize(size_t number_versions) { + return kPublicFlagsSize + PACKET_8BYTE_GUID + + number_versions * kQuicVersionSize; +} + +// static +bool QuicFramer::CanTruncate(const QuicFrame& frame, size_t free_bytes) { + // TODO(ianswett): GetMinConnectionCloseFrameSize may be incorrect, because + // checking for it here results in frames not being added, but the resulting + // frames do actually fit. + if ((frame.type == ACK_FRAME || frame.type == CONNECTION_CLOSE_FRAME) && + free_bytes >= GetMinAckFrameSize()) { + return true; + } + return false; +} + bool QuicFramer::IsSupportedVersion(const QuicVersion version) const { for (size_t i = 0; i < arraysize(kSupportedQuicVersions); ++i) { if (version == kSupportedQuicVersions[i]) { @@ -151,11 +207,6 @@ bool QuicFramer::IsSupportedVersion(const QuicVersion version) const { return false; } -size_t QuicFramer::GetVersionNegotiationPacketSize(size_t number_versions) { - return kPublicFlagsSize + PACKET_8BYTE_GUID + - number_versions * kQuicVersionSize; -} - size_t QuicFramer::GetSerializedFrameLength( const QuicFrame& frame, size_t free_bytes, bool first_frame) { if (frame.type == PADDING_FRAME) { @@ -164,20 +215,21 @@ size_t QuicFramer::GetSerializedFrameLength( } // See if it fits as the non-last frame. size_t frame_len = ComputeFrameLength(frame, false); + // STREAM frames save two bytes when they're the last frame in the packet. + if (frame_len > free_bytes && frame.type == STREAM_FRAME) { + frame_len = ComputeFrameLength(frame, true); + } if (frame_len > free_bytes) { // Only truncate the first frame in a packet, so if subsequent ones go // over, stop including more frames. if (!first_frame) { return 0; } - if (CanTruncate(frame)) { + if (CanTruncate(frame, free_bytes)) { // Truncate the frame so the packet will not exceed kMaxPacketSize. // Note that we may not use every byte of the writer in this case. - if (free_bytes >= GetMinAckFrameSize()) { - DLOG(INFO) << "Truncating large frame"; - return free_bytes; - } - return 0; + DLOG(INFO) << "Truncating large frame"; + return free_bytes; } } return frame_len; @@ -193,7 +245,7 @@ QuicPacketEntropyHash QuicFramer::GetPacketEntropyHash( return 1 << (header.packet_sequence_number % 8); } -SerializedPacket QuicFramer::ConstructFrameDataPacket( +SerializedPacket QuicFramer::BuildUnsizedDataPacket( const QuicPacketHeader& header, const QuicFrames& frames) { const size_t max_plaintext_size = GetMaxPlaintextSize(kMaxPacketSize); @@ -205,15 +257,15 @@ SerializedPacket QuicFramer::ConstructFrameDataPacket( DCHECK(frame_size); packet_size += frame_size; } - return ConstructFrameDataPacket(header, frames, packet_size); + return BuildDataPacket(header, frames, packet_size); } -SerializedPacket QuicFramer::ConstructFrameDataPacket( +SerializedPacket QuicFramer::BuildDataPacket( const QuicPacketHeader& header, const QuicFrames& frames, size_t packet_size) { QuicDataWriter writer(packet_size); - SerializedPacket kNoPacket = SerializedPacket(0, NULL, 0, NULL); + const SerializedPacket kNoPacket(0, NULL, 0, NULL); if (!WritePacketHeader(header, &writer)) { return kNoPacket; } @@ -221,7 +273,8 @@ SerializedPacket QuicFramer::ConstructFrameDataPacket( for (size_t i = 0; i < frames.size(); ++i) { const QuicFrame& frame = frames[i]; - if (!writer.WriteUInt8(frame.type)) { + const bool last_frame_in_packet = i == (frames.size() - 1); + if (!AppendTypeByte(frame, last_frame_in_packet, &writer)) { return kNoPacket; } @@ -230,9 +283,15 @@ SerializedPacket QuicFramer::ConstructFrameDataPacket( writer.WritePadding(); break; case STREAM_FRAME: - if (!AppendStreamFramePayload( - *frame.stream_frame, &writer)) { - return kNoPacket; + if (quic_version_ == QUIC_VERSION_6) { + if (!AppendV6StreamFramePayload(*frame.stream_frame, &writer)) { + return kNoPacket; + } + } else { + if (!AppendStreamFramePayload( + *frame.stream_frame, last_frame_in_packet, &writer)) { + return kNoPacket; + } } break; case ACK_FRAME: @@ -287,9 +346,8 @@ SerializedPacket QuicFramer::ConstructFrameDataPacket( GetPacketEntropyHash(header), NULL); } -SerializedPacket QuicFramer::ConstructFecPacket( - const QuicPacketHeader& header, - const QuicFecData& fec) { +SerializedPacket QuicFramer::BuildFecPacket(const QuicPacketHeader& header, + const QuicFecData& fec) { DCHECK_EQ(IN_FEC_GROUP, header.is_in_fec_group); DCHECK_NE(0u, header.fec_group); size_t len = GetPacketHeaderSize(header); @@ -315,7 +373,7 @@ SerializedPacket QuicFramer::ConstructFecPacket( } // static -QuicEncryptedPacket* QuicFramer::ConstructPublicResetPacket( +QuicEncryptedPacket* QuicFramer::BuildPublicResetPacket( const QuicPublicResetPacket& packet) { DCHECK(packet.public_header.reset_flag); size_t len = GetPublicResetPacketSize(); @@ -345,7 +403,7 @@ QuicEncryptedPacket* QuicFramer::ConstructPublicResetPacket( return new QuicEncryptedPacket(writer.take(), len, true); } -QuicEncryptedPacket* QuicFramer::ConstructVersionNegotiationPacket( +QuicEncryptedPacket* QuicFramer::BuildVersionNegotiationPacket( const QuicPacketPublicHeader& header, const QuicVersionVector& supported_versions) { DCHECK(header.version_flag); @@ -796,6 +854,11 @@ bool QuicFramer::ProcessPacketHeader( set_detailed_error("Unable to read first fec protected packet offset."); return RaiseError(QUIC_INVALID_PACKET_HEADER); } + if (first_fec_protected_packet_offset >= header->packet_sequence_number) { + set_detailed_error("First fec protected packet offset must be less " + "than the sequence number."); + return RaiseError(QUIC_INVALID_PACKET_HEADER); + } header->fec_group = header->packet_sequence_number - first_fec_protected_packet_offset; } @@ -834,58 +897,112 @@ bool QuicFramer::ProcessFrameData() { set_detailed_error("Unable to read frame type."); return RaiseError(QUIC_INVALID_FRAME_DATA); } + + if (quic_version_ >= QUIC_VERSION_7) { + if ((frame_type & kQuicFrameType0BitMask) == 0) { + QuicStreamFrame frame; + if (!ProcessStreamFrame(frame_type, &frame)) { + return RaiseError(QUIC_INVALID_FRAME_DATA); + } + if (!visitor_->OnStreamFrame(frame)) { + DLOG(INFO) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + continue; + } + + frame_type >>= 1; + if ((frame_type & kQuicFrameType0BitMask) == 0) { + QuicAckFrame frame; + if (!ProcessAckFrame(&frame)) { + return RaiseError(QUIC_INVALID_FRAME_DATA); + } + if (!visitor_->OnAckFrame(frame)) { + DLOG(INFO) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + continue; + } + + frame_type >>= 1; + if ((frame_type & kQuicFrameType0BitMask) == 0) { + QuicCongestionFeedbackFrame frame; + if (!ProcessQuicCongestionFeedbackFrame(&frame)) { + return RaiseError(QUIC_INVALID_FRAME_DATA); + } + if (!visitor_->OnCongestionFeedbackFrame(frame)) { + DLOG(INFO) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + continue; + } + + frame_type >>= 1; + } + switch (frame_type) { case PADDING_FRAME: // We're done with the packet return true; + // STREAM_FRAME, ACK_FRAME, and CONGESTION_FEEDBACK handled above for + // QUIC_VERSION_7 and later. case STREAM_FRAME: { QuicStreamFrame frame; - if (!ProcessStreamFrame(&frame)) { + if (!ProcessV6StreamFrame(&frame)) { return RaiseError(QUIC_INVALID_FRAME_DATA); } if (!visitor_->OnStreamFrame(frame)) { - DLOG(INFO) << "Visitor asked to stopped further processing."; + DLOG(INFO) << "Visitor asked to stop further processing."; // Returning true since there was no parsing error. return true; } - break; + continue; } + case ACK_FRAME: { QuicAckFrame frame; if (!ProcessAckFrame(&frame)) { return RaiseError(QUIC_INVALID_FRAME_DATA); } if (!visitor_->OnAckFrame(frame)) { - DLOG(INFO) << "Visitor asked to stopped further processing."; + DLOG(INFO) << "Visitor asked to stop further processing."; // Returning true since there was no parsing error. + // TODO(ianswett): Consider continuing to process frames, since there + // was not a parsing error. return true; } - break; + continue; } + case CONGESTION_FEEDBACK_FRAME: { QuicCongestionFeedbackFrame frame; if (!ProcessQuicCongestionFeedbackFrame(&frame)) { return RaiseError(QUIC_INVALID_FRAME_DATA); } if (!visitor_->OnCongestionFeedbackFrame(frame)) { - DLOG(INFO) << "Visitor asked to stopped further processing."; + DLOG(INFO) << "Visitor asked to stop further processing."; // Returning true since there was no parsing error. return true; } - break; + continue; } + case RST_STREAM_FRAME: { QuicRstStreamFrame frame; if (!ProcessRstStreamFrame(&frame)) { return RaiseError(QUIC_INVALID_RST_STREAM_DATA); } if (!visitor_->OnRstStreamFrame(frame)) { - DLOG(INFO) << "Visitor asked to stopped further processing."; + DLOG(INFO) << "Visitor asked to stop further processing."; // Returning true since there was no parsing error. return true; } - break; + continue; } + case CONNECTION_CLOSE_FRAME: { QuicConnectionCloseFrame frame; if (!ProcessConnectionCloseFrame(&frame)) { @@ -893,42 +1010,90 @@ bool QuicFramer::ProcessFrameData() { } if (!visitor_->OnAckFrame(frame.ack_frame)) { - DLOG(INFO) << "Visitor asked to stopped further processing."; + DLOG(INFO) << "Visitor asked to stop further processing."; // Returning true since there was no parsing error. return true; } if (!visitor_->OnConnectionCloseFrame(frame)) { - DLOG(INFO) << "Visitor asked to stopped further processing."; + DLOG(INFO) << "Visitor asked to stop further processing."; // Returning true since there was no parsing error. return true; } - break; + continue; } + case GOAWAY_FRAME: { QuicGoAwayFrame goaway_frame; if (!ProcessGoAwayFrame(&goaway_frame)) { return RaiseError(QUIC_INVALID_GOAWAY_DATA); } if (!visitor_->OnGoAwayFrame(goaway_frame)) { - DLOG(INFO) << "Visitor asked to stopped further processing."; + DLOG(INFO) << "Visitor asked to stop further processing."; // Returning true since there was no parsing error. return true; } - break; + continue; } - default: - set_detailed_error("Illegal frame type."); - DLOG(WARNING) << "Illegal frame type: " - << static_cast<int>(frame_type); - return RaiseError(QUIC_INVALID_FRAME_DATA); + + set_detailed_error("Illegal frame type."); + DLOG(WARNING) << "Illegal frame type: " + << static_cast<int>(frame_type); + return RaiseError(QUIC_INVALID_FRAME_DATA); } } return true; } -bool QuicFramer::ProcessStreamFrame(QuicStreamFrame* frame) { +bool QuicFramer::ProcessStreamFrame(uint8 frame_type, + QuicStreamFrame* frame) { + uint8 stream_flags = frame_type >> 1; + // Read from right to left: StreamID, Offset, Data Length, Fin. + const uint8 stream_id_length = (stream_flags & kQuicStreamIDLengthMask) + 1; + stream_flags >>= kQuicStreamIdShift; + + uint8 offset_length = (stream_flags & kQuicStreamOffsetMask); + // There is no encoding for 1 byte, only 0 and 2 through 8. + if (offset_length > 0) { + offset_length += 1; + } + stream_flags >>= kQuicStreamOffsetShift; + + bool has_data_length = + (stream_flags & kQuicStreamDataLengthMask) == kQuicStreamDataLengthMask; + stream_flags >>= kQuicStreamDataLengthShift; + + frame->fin = (stream_flags & kQuicStreamFinMask) == kQuicStreamFinShift; + + frame->stream_id = 0; + if (!reader_->ReadBytes(&frame->stream_id, stream_id_length)) { + set_detailed_error("Unable to read stream_id."); + return false; + } + + frame->offset = 0; + if (!reader_->ReadBytes(&frame->offset, offset_length)) { + set_detailed_error("Unable to read offset."); + return false; + } + + if (has_data_length) { + if (!reader_->ReadStringPiece16(&frame->data)) { + set_detailed_error("Unable to read frame data."); + return false; + } + } else { + if (!reader_->ReadStringPiece(&frame->data, reader_->BytesRemaining())) { + set_detailed_error("Unable to read frame data."); + return false; + } + } + + return true; +} + +bool QuicFramer::ProcessV6StreamFrame(QuicStreamFrame* frame) { if (!reader_->ReadUInt32(&frame->stream_id)) { set_detailed_error("Unable to read stream_id."); return false; @@ -1361,12 +1526,14 @@ bool QuicFramer::DecryptPayload(const QuicPacketHeader& header, return true; } -size_t QuicFramer::ComputeFrameLength(const QuicFrame& frame, bool last_frame) { +size_t QuicFramer::ComputeFrameLength(const QuicFrame& frame, + bool last_frame_in_packet) { switch (frame.type) { case STREAM_FRAME: - return GetMinStreamFrameSize(frame.stream_frame->stream_id, + return GetMinStreamFrameSize(quic_version_, + frame.stream_frame->stream_id, frame.stream_frame->offset, - last_frame) + + last_frame_in_packet) + frame.stream_frame->data.size(); case ACK_FRAME: { const QuicAckFrame& ack = *frame.ack_frame; @@ -1432,6 +1599,59 @@ size_t QuicFramer::ComputeFrameLength(const QuicFrame& frame, bool last_frame) { return 0; } +bool QuicFramer::AppendTypeByte(const QuicFrame& frame, + bool last_frame_in_packet, + QuicDataWriter* writer) { + if (quic_version_ == QUIC_VERSION_6) { + return writer->WriteUInt8(frame.type); + } + + uint8 type_byte = 0; + switch (frame.type) { + case STREAM_FRAME: { + if (frame.stream_frame == NULL) { + LOG(DFATAL) << "Failed to append STREAM frame with no stream_frame."; + } + // Fin bit. + type_byte |= frame.stream_frame->fin ? kQuicStreamFinMask : 0; + + // Data Length bit. + type_byte <<= kQuicStreamDataLengthShift; + type_byte |= last_frame_in_packet ? 0 : kQuicStreamDataLengthMask; + + // Offset 3 bits. + type_byte <<= kQuicStreamOffsetShift; + const size_t offset_len = GetStreamOffsetSize(frame.stream_frame->offset); + if (offset_len > 0) { + type_byte |= offset_len - 1; + } + + // stream id 2 bits. + type_byte <<= kQuicStreamIdShift; + type_byte |= GetStreamIdSize(frame.stream_frame->stream_id) - 1; + + type_byte <<= 1; // Leaves the last bit as a 0. + break; + } + case ACK_FRAME: { + // TODO(ianswett): Use extra 5 bits in the ack framing. + type_byte = 0x01; + break; + } + case CONGESTION_FEEDBACK_FRAME: { + // TODO(ianswett): Use extra 5 bits in the congestion feedback framing. + type_byte = 0x03; + break; + } + default: + type_byte = + frame.type << kQuicDefaultFrameTypeShift | kQuicDefaultFrameTypeMask; + break; + } + + return writer->WriteUInt8(type_byte); +} + // static bool QuicFramer::AppendPacketSequenceNumber( QuicSequenceNumberLength sequence_number_length, @@ -1467,6 +1687,27 @@ bool QuicFramer::AppendPacketSequenceNumber( bool QuicFramer::AppendStreamFramePayload( const QuicStreamFrame& frame, + bool last_frame_in_packet, + QuicDataWriter* writer) { + if (!writer->WriteBytes(&frame.stream_id, GetStreamIdSize(frame.stream_id))) { + return false; + } + if (!writer->WriteBytes(&frame.offset, GetStreamOffsetSize(frame.offset))) { + return false; + } + if (!last_frame_in_packet) { + if (!writer->WriteUInt16(frame.data.size())) { + return false; + } + } + if (!writer->WriteBytes(frame.data.data(), frame.data.size())) { + return false; + } + return true; +} + +bool QuicFramer::AppendV6StreamFramePayload( + const QuicStreamFrame& frame, QuicDataWriter* writer) { if (!writer->WriteUInt32(frame.stream_id)) { return false; diff --git a/net/quic/quic_framer.h b/net/quic/quic_framer.h index b62d56f..f2de9da 100644 --- a/net/quic/quic_framer.h +++ b/net/quic/quic_framer.h @@ -33,12 +33,13 @@ const size_t kQuicErrorCodeSize = 4; // Number of bytes reserved to denote the length of error details field. const size_t kQuicErrorDetailsLengthSize = 2; -// Number of bytes reserved for stream id -const size_t kQuicStreamIdSize = 4; +// Maximum number of bytes reserved for stream id. +const size_t kQuicMaxStreamIdSize = 4; // Number of bytes reserved for fin flag in stream frame. +// TODO(ianswett): Remove once QUIC_VERSION_6 and before are removed. const size_t kQuicStreamFinSize = 1; -// Number of bytes reserved for byte offset in stream frame. -const size_t kQuicStreamOffsetSize = 8; +// Maximum number of bytes reserved for byte offset in stream frame. +const size_t kQuicMaxStreamOffsetSize = 8; // Number of bytes reserved to store payload length in stream frame. const size_t kQuicStreamPayloadLengthSize = 2; @@ -226,12 +227,11 @@ class NET_EXPORT_PRIVATE QuicFramer { bool ProcessRevivedPacket(QuicPacketHeader* header, base::StringPiece payload); - // Size in bytes of all stream frame fields without the payload. - static size_t GetMinStreamFrameSize(); - // Size in bytes of all stream frame fields without the payload. - static size_t GetMinStreamFrameSize(QuicStreamId stream_id, + // Largest size in bytes of all stream frame fields without the payload. + static size_t GetMinStreamFrameSize(QuicVersion version, + QuicStreamId stream_id, QuicStreamOffset offset, - bool last_frame); + bool last_frame_in_packet); // Size in bytes of all ack frame fields without the missing packets. static size_t GetMinAckFrameSize(); // Size in bytes of all reset stream frame without the error details. @@ -249,7 +249,10 @@ class NET_EXPORT_PRIVATE QuicFramer { // Size in bytes required to serialize the stream offset. static size_t GetStreamOffsetSize(QuicStreamOffset offset); // Size in bytes required for a serialized version negotiation packet - size_t GetVersionNegotiationPacketSize(size_t number_versions); + static size_t GetVersionNegotiationPacketSize(size_t number_versions); + + + static bool CanTruncate(const QuicFrame& frame, size_t free_bytes); // Returns the number of bytes added to the packet for the specified frame, // and 0 if the frame doesn't fit. Includes the header size for the first @@ -269,27 +272,27 @@ class NET_EXPORT_PRIVATE QuicFramer { // and is populated with the fields in |header| and |frames|, or is NULL if // the packet could not be created. // TODO(ianswett): Used for testing only. - SerializedPacket ConstructFrameDataPacket(const QuicPacketHeader& header, - const QuicFrames& frames); + SerializedPacket BuildUnsizedDataPacket(const QuicPacketHeader& header, + const QuicFrames& frames); // Returns a SerializedPacket whose |packet| member is owned by the caller, // is created from the first |num_frames| frames, or is NULL if the packet // could not be created. The packet must be of size |packet_size|. - SerializedPacket ConstructFrameDataPacket(const QuicPacketHeader& header, - const QuicFrames& frames, - size_t packet_size); + SerializedPacket BuildDataPacket(const QuicPacketHeader& header, + const QuicFrames& frames, + size_t packet_size); // Returns a SerializedPacket whose |packet| member is owned by the caller, // and is populated with the fields in |header| and |fec|, or is NULL if the // packet could not be created. - SerializedPacket ConstructFecPacket(const QuicPacketHeader& header, - const QuicFecData& fec); + SerializedPacket BuildFecPacket(const QuicPacketHeader& header, + const QuicFecData& fec); // Returns a new public reset packet, owned by the caller. - static QuicEncryptedPacket* ConstructPublicResetPacket( + static QuicEncryptedPacket* BuildPublicResetPacket( const QuicPublicResetPacket& packet); - QuicEncryptedPacket* ConstructVersionNegotiationPacket( + QuicEncryptedPacket* BuildVersionNegotiationPacket( const QuicPacketPublicHeader& header, const QuicVersionVector& supported_versions); @@ -361,7 +364,8 @@ class NET_EXPORT_PRIVATE QuicFramer { QuicSequenceNumberLength sequence_number_length, QuicPacketSequenceNumber* sequence_number); bool ProcessFrameData(); - bool ProcessStreamFrame(QuicStreamFrame* frame); + bool ProcessStreamFrame(uint8 frame_type, QuicStreamFrame* frame); + bool ProcessV6StreamFrame(QuicStreamFrame* frame); bool ProcessAckFrame(QuicAckFrame* frame); bool ProcessReceivedInfo(ReceivedPacketInfo* received_info); bool ProcessSentInfo(SentPacketInfo* sent_info); @@ -381,15 +385,21 @@ class NET_EXPORT_PRIVATE QuicFramer { QuicPacketSequenceNumber packet_sequence_number) const; // Computes the wire size in bytes of the payload of |frame|. - size_t ComputeFrameLength(const QuicFrame& frame, bool last_frame); + size_t ComputeFrameLength(const QuicFrame& frame, bool last_frame_in_packet); static bool AppendPacketSequenceNumber( QuicSequenceNumberLength sequence_number_length, QuicPacketSequenceNumber packet_sequence_number, QuicDataWriter* writer); + bool AppendTypeByte(const QuicFrame& frame, + bool last_frame_in_packet, + QuicDataWriter* writer); bool AppendStreamFramePayload(const QuicStreamFrame& frame, + bool last_frame_in_packet, QuicDataWriter* builder); + bool AppendV6StreamFramePayload(const QuicStreamFrame& frame, + QuicDataWriter* builder); bool AppendAckFramePayload(const QuicAckFrame& frame, QuicDataWriter* builder); bool AppendQuicCongestionFeedbackFramePayload( diff --git a/net/quic/quic_framer_test.cc b/net/quic/quic_framer_test.cc index b7a4e30..513e730 100644 --- a/net/quic/quic_framer_test.cc +++ b/net/quic/quic_framer_test.cc @@ -43,6 +43,8 @@ const size_t kGuidOffset = kPublicFlagsSize; // Index into the version string in the header. (if present). const size_t kVersionOffset = kGuidOffset + PACKET_8BYTE_GUID; +// Size in bytes of the stream frame fields for an arbitrary StreamID and +// offset and the last frame in a packet. // Index into the sequence number offset in the header. size_t GetSequenceNumberOffset(QuicGuidLength guid_length, bool include_version) { @@ -317,8 +319,8 @@ class QuicFramerTest : public ::testing::TestWithParam<QuicVersion> { framer_.set_visitor(&visitor_); framer_.set_received_entropy_calculator(&entropy_calculator_); - QuicVersion version = GetParam(); - framer_.set_version(version); + version_ = GetParam(); + framer_.set_version(version_); } bool CheckEncryption(QuicPacketSequenceNumber sequence_number, @@ -412,16 +414,17 @@ class QuicFramerTest : public ::testing::TestWithParam<QuicVersion> { test::TestEncrypter* encrypter_; test::TestDecrypter* decrypter_; + QuicVersion version_; QuicTime start_; QuicFramer framer_; test::TestQuicVisitor visitor_; test::TestEntropyCalculator entropy_calculator_; }; -// Run all framer tests with QUIC version 6. +// Run all framer tests with QUIC version 6 and 7. INSTANTIATE_TEST_CASE_P(QuicFramerTests, QuicFramerTest, - ::testing::Values(QUIC_VERSION_6)); + ::testing::Values(QUIC_VERSION_6, QUIC_VERSION_7)); TEST_P(QuicFramerTest, CalculatePacketSequenceNumberFromWireNearEpochStart) { // A few quick manual sanity checks @@ -778,7 +781,7 @@ TEST_P(QuicFramerTest, PacketHeaderWith0ByteGuid) { TEST_P(QuicFramerTest, PacketHeaderWithVersionFlag) { // Set a specific version. - framer_.set_version(QUIC_VERSION_6); + framer_.set_version(QUIC_VERSION_7); unsigned char packet[] = { // public flags (version) @@ -787,7 +790,7 @@ TEST_P(QuicFramerTest, PacketHeaderWithVersionFlag) { 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // version tag - 'Q', '0', '0', '6', + 'Q', '0', '0', '7', // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -803,7 +806,7 @@ TEST_P(QuicFramerTest, PacketHeaderWithVersionFlag) { visitor_.header_->public_header.guid); EXPECT_FALSE(visitor_.header_->public_header.reset_flag); EXPECT_TRUE(visitor_.header_->public_header.version_flag); - EXPECT_EQ(QUIC_VERSION_6, visitor_.header_->public_header.versions[0]); + EXPECT_EQ(QUIC_VERSION_7, visitor_.header_->public_header.versions[0]); EXPECT_FALSE(visitor_.header_->fec_flag); EXPECT_FALSE(visitor_.header_->entropy_flag); EXPECT_EQ(0, visitor_.header_->entropy_hash); @@ -1039,7 +1042,7 @@ TEST_P(QuicFramerTest, InvalidPublicFlag) { TEST_P(QuicFramerTest, InvalidPublicFlagWithMatchingVersions) { // Set a specific version. - framer_.set_version(QUIC_VERSION_6); + framer_.set_version(QUIC_VERSION_7); unsigned char packet[] = { // public flags (8 byte guid and version flag and an unknown flag) @@ -1048,7 +1051,7 @@ TEST_P(QuicFramerTest, InvalidPublicFlagWithMatchingVersions) { 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // version tag - 'Q', '0', '0', '6', + 'Q', '0', '0', '7', // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -1095,7 +1098,7 @@ TEST_P(QuicFramerTest, LargePublicFlagWithMismatchedVersions) { 0x00, // frame type (padding frame) - 0x00, + static_cast<unsigned char>((version_ == QUIC_VERSION_6) ? 0x00 : 0x07), }; QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); EXPECT_TRUE(framer_.ProcessPacket(encrypted)); @@ -1142,6 +1145,28 @@ TEST_P(QuicFramerTest, InvalidPrivateFlag) { QUIC_INVALID_PACKET_HEADER); }; +TEST_P(QuicFramerTest, InvalidFECGroupOffset) { + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, + // private flags (fec group) + 0x02, + // first fec protected packet offset + 0x10 + }; + CheckProcessingFails(packet, + arraysize(packet), + "First fec protected packet offset must be less " + "than the sequence number.", + QUIC_INVALID_PACKET_HEADER); +}; + TEST_P(QuicFramerTest, PaddingFrame) { unsigned char packet[] = { // public flags (8 byte guid) @@ -1156,7 +1181,7 @@ TEST_P(QuicFramerTest, PaddingFrame) { 0x00, // frame type (padding frame) - 0x00, + static_cast<unsigned char>((version_ == QUIC_VERSION_6) ? 0x00 : 0x07), // Ignored data (which in this case is a stream frame) 0x01, 0x04, 0x03, 0x02, 0x01, @@ -1185,7 +1210,10 @@ TEST_P(QuicFramerTest, PaddingFrame) { "Unable to read frame type.", QUIC_INVALID_FRAME_DATA); } -TEST_P(QuicFramerTest, StreamFrame) { +TEST_P(QuicFramerTest, StreamFrameVersion6) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_6); + unsigned char packet[] = { // public flags (8 byte guid) 0x3C, @@ -1232,17 +1260,17 @@ TEST_P(QuicFramerTest, StreamFrame) { EXPECT_EQ("hello world!", visitor_.stream_frames_[0]->data); // Now test framing boundaries - for (size_t i = 0; i < QuicFramer::GetMinStreamFrameSize(); ++i) { + for (size_t i = 0; i < GetMinStreamFrameSize(framer_.version()); ++i) { string expected_error; if (i < kQuicFrameTypeSize) { expected_error = "Unable to read frame type."; - } else if (i < kQuicFrameTypeSize + kQuicStreamIdSize) { + } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize) { expected_error = "Unable to read stream_id."; - } else if (i < kQuicFrameTypeSize + kQuicStreamIdSize + + } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicStreamFinSize) { expected_error = "Unable to read fin."; - } else if (i < kQuicFrameTypeSize + kQuicStreamIdSize + - kQuicStreamFinSize + kQuicStreamOffsetSize) { + } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize + + kQuicStreamFinSize + kQuicMaxStreamOffsetSize) { expected_error = "Unable to read offset."; } else { expected_error = "Unable to read frame data."; @@ -1255,7 +1283,290 @@ TEST_P(QuicFramerTest, StreamFrame) { } } -TEST_P(QuicFramerTest, StreamFrameWithVersion) { +TEST_P(QuicFramerTest, StreamFrame) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_7); + + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags + 0x00, + + // frame type (stream frame with fin) + 0xFE, + // stream id + 0x04, 0x03, 0x02, 0x01, + // offset + 0x54, 0x76, 0x10, 0x32, + 0xDC, 0xFE, 0x98, 0xBA, + // data length + 0x0c, 0x00, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); + + ASSERT_EQ(1u, visitor_.stream_frames_.size()); + EXPECT_EQ(0u, visitor_.ack_frames_.size()); + EXPECT_EQ(static_cast<uint64>(0x01020304), + visitor_.stream_frames_[0]->stream_id); + EXPECT_TRUE(visitor_.stream_frames_[0]->fin); + EXPECT_EQ(GG_UINT64_C(0xBA98FEDC32107654), + visitor_.stream_frames_[0]->offset); + EXPECT_EQ("hello world!", visitor_.stream_frames_[0]->data); + + // Now test framing boundaries + for (size_t i = 0; i < GetMinStreamFrameSize(framer_.version()); ++i) { + string expected_error; + if (i < kQuicFrameTypeSize) { + expected_error = "Unable to read frame type."; + } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize) { + expected_error = "Unable to read stream_id."; + } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize) { + expected_error = "Unable to read fin."; + } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize + + kQuicMaxStreamOffsetSize) { + expected_error = "Unable to read offset."; + } else { + expected_error = "Unable to read frame data."; + } + CheckProcessingFails( + packet, + i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_FRAME_DATA); + } +} + +TEST_P(QuicFramerTest, StreamFrame3ByteStreamId) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_7); + + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags + 0x00, + + // frame type (stream frame with fin) + 0xFC, + // stream id + 0x04, 0x03, 0x02, + // offset + 0x54, 0x76, 0x10, 0x32, + 0xDC, 0xFE, 0x98, 0xBA, + // data length + 0x0c, 0x00, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); + + ASSERT_EQ(1u, visitor_.stream_frames_.size()); + EXPECT_EQ(0u, visitor_.ack_frames_.size()); + EXPECT_EQ(static_cast<uint64>(0x00020304), + visitor_.stream_frames_[0]->stream_id); + EXPECT_TRUE(visitor_.stream_frames_[0]->fin); + EXPECT_EQ(GG_UINT64_C(0xBA98FEDC32107654), + visitor_.stream_frames_[0]->offset); + EXPECT_EQ("hello world!", visitor_.stream_frames_[0]->data); + + // Now test framing boundaries + const size_t stream_id_size = 3; + for (size_t i = 0; i < GetMinStreamFrameSize(framer_.version()); ++i) { + string expected_error; + if (i < kQuicFrameTypeSize) { + expected_error = "Unable to read frame type."; + } else if (i < kQuicFrameTypeSize + stream_id_size) { + expected_error = "Unable to read stream_id."; + } else if (i < kQuicFrameTypeSize + stream_id_size - 1) { + expected_error = "Unable to read fin."; + } else if (i < kQuicFrameTypeSize + stream_id_size + + kQuicMaxStreamOffsetSize) { + expected_error = "Unable to read offset."; + } else { + expected_error = "Unable to read frame data."; + } + CheckProcessingFails( + packet, + i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_FRAME_DATA); + } +} + +TEST_P(QuicFramerTest, StreamFrame2ByteStreamId) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_7); + + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags + 0x00, + + // frame type (stream frame with fin) + 0xFA, + // stream id + 0x04, 0x03, + // offset + 0x54, 0x76, 0x10, 0x32, + 0xDC, 0xFE, 0x98, 0xBA, + // data length + 0x0c, 0x00, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); + + ASSERT_EQ(1u, visitor_.stream_frames_.size()); + EXPECT_EQ(0u, visitor_.ack_frames_.size()); + EXPECT_EQ(static_cast<uint64>(0x00000304), + visitor_.stream_frames_[0]->stream_id); + EXPECT_TRUE(visitor_.stream_frames_[0]->fin); + EXPECT_EQ(GG_UINT64_C(0xBA98FEDC32107654), + visitor_.stream_frames_[0]->offset); + EXPECT_EQ("hello world!", visitor_.stream_frames_[0]->data); + + // Now test framing boundaries + const size_t stream_id_size = 2; + for (size_t i = 0; i < GetMinStreamFrameSize(framer_.version()); ++i) { + string expected_error; + if (i < kQuicFrameTypeSize) { + expected_error = "Unable to read frame type."; + } else if (i < kQuicFrameTypeSize + stream_id_size) { + expected_error = "Unable to read stream_id."; + } else if (i < kQuicFrameTypeSize + stream_id_size - 1) { + expected_error = "Unable to read fin."; + } else if (i < kQuicFrameTypeSize + stream_id_size + + kQuicMaxStreamOffsetSize) { + expected_error = "Unable to read offset."; + } else { + expected_error = "Unable to read frame data."; + } + CheckProcessingFails( + packet, + i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_FRAME_DATA); + } +} + +TEST_P(QuicFramerTest, StreamFrame1ByteStreamId) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_7); + + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags + 0x00, + + // frame type (stream frame with fin) + 0xF8, + // stream id + 0x04, + // offset + 0x54, 0x76, 0x10, 0x32, + 0xDC, 0xFE, 0x98, 0xBA, + // data length + 0x0c, 0x00, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); + + ASSERT_EQ(1u, visitor_.stream_frames_.size()); + EXPECT_EQ(0u, visitor_.ack_frames_.size()); + EXPECT_EQ(static_cast<uint64>(0x00000004), + visitor_.stream_frames_[0]->stream_id); + EXPECT_TRUE(visitor_.stream_frames_[0]->fin); + EXPECT_EQ(GG_UINT64_C(0xBA98FEDC32107654), + visitor_.stream_frames_[0]->offset); + EXPECT_EQ("hello world!", visitor_.stream_frames_[0]->data); + + // Now test framing boundaries + const size_t stream_id_size = 1; + for (size_t i = 0; i < GetMinStreamFrameSize(framer_.version()); ++i) { + string expected_error; + if (i < kQuicFrameTypeSize) { + expected_error = "Unable to read frame type."; + } else if (i < kQuicFrameTypeSize + stream_id_size) { + expected_error = "Unable to read stream_id."; + } else if (i < kQuicFrameTypeSize + stream_id_size - 1) { + expected_error = "Unable to read fin."; + } else if (i < kQuicFrameTypeSize + stream_id_size + + kQuicMaxStreamOffsetSize) { + expected_error = "Unable to read offset."; + } else { + expected_error = "Unable to read frame data."; + } + CheckProcessingFails( + packet, + i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_FRAME_DATA); + } +} + +TEST_P(QuicFramerTest, StreamFrameWithVersion6) { // Set a specific version. framer_.set_version(QUIC_VERSION_6); @@ -1309,17 +1620,89 @@ TEST_P(QuicFramerTest, StreamFrameWithVersion) { EXPECT_EQ("hello world!", visitor_.stream_frames_[0]->data); // Now test framing boundaries - for (size_t i = 0; i < QuicFramer::GetMinStreamFrameSize(); ++i) { + for (size_t i = 0; i < GetMinStreamFrameSize(framer_.version()); ++i) { string expected_error; if (i < kQuicFrameTypeSize) { expected_error = "Unable to read frame type."; - } else if (i < kQuicFrameTypeSize + kQuicStreamIdSize) { + } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize) { expected_error = "Unable to read stream_id."; - } else if (i < kQuicFrameTypeSize + kQuicStreamIdSize + + } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicStreamFinSize) { expected_error = "Unable to read fin."; - } else if (i < kQuicFrameTypeSize + kQuicStreamIdSize + - kQuicStreamFinSize + kQuicStreamOffsetSize) { + } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize + + kQuicStreamFinSize + kQuicMaxStreamOffsetSize) { + expected_error = "Unable to read offset."; + } else { + expected_error = "Unable to read frame data."; + } + CheckProcessingFails( + packet, + i + GetPacketHeaderSize(PACKET_8BYTE_GUID, kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_FRAME_DATA); + } +} + +TEST_P(QuicFramerTest, StreamFrameWithVersion) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_7); + + unsigned char packet[] = { + // public flags (version, 8 byte guid) + 0x3D, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // version tag + 'Q', '0', '0', '7', + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags + 0x00, + + // frame type (stream frame with fin) + 0xFE, + // stream id + 0x04, 0x03, 0x02, 0x01, + // offset + 0x54, 0x76, 0x10, 0x32, + 0xDC, 0xFE, 0x98, 0xBA, + // data length + 0x0c, 0x00, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(visitor_.header_.get()->public_header.version_flag); + EXPECT_EQ(QUIC_VERSION_7, visitor_.header_.get()->public_header.versions[0]); + EXPECT_TRUE(CheckDecryption(encrypted, kIncludeVersion)); + + ASSERT_EQ(1u, visitor_.stream_frames_.size()); + EXPECT_EQ(0u, visitor_.ack_frames_.size()); + EXPECT_EQ(static_cast<uint64>(0x01020304), + visitor_.stream_frames_[0]->stream_id); + EXPECT_TRUE(visitor_.stream_frames_[0]->fin); + EXPECT_EQ(GG_UINT64_C(0xBA98FEDC32107654), + visitor_.stream_frames_[0]->offset); + EXPECT_EQ("hello world!", visitor_.stream_frames_[0]->data); + + // Now test framing boundaries + for (size_t i = 0; i < GetMinStreamFrameSize(framer_.version()); ++i) { + string expected_error; + if (i < kQuicFrameTypeSize) { + expected_error = "Unable to read frame type."; + } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize) { + expected_error = "Unable to read stream_id."; + } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize + + kQuicMaxStreamOffsetSize) { expected_error = "Unable to read offset."; } else { expected_error = "Unable to read frame data."; @@ -1347,12 +1730,10 @@ TEST_P(QuicFramerTest, RejectPacket) { // private flags 0x00, - // frame type (stream frame) - 0x01, + // frame type (stream frame with fin) + 0xFE, // stream id 0x04, 0x03, 0x02, 0x01, - // fin - 0x01, // offset 0x54, 0x76, 0x10, 0x32, 0xDC, 0xFE, 0x98, 0xBA, @@ -1376,13 +1757,14 @@ TEST_P(QuicFramerTest, RejectPacket) { } TEST_P(QuicFramerTest, RevivedStreamFrame) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_7); + unsigned char payload[] = { - // frame type (stream frame) - 0x01, + // frame type (stream frame with fin) + 0xFE, // stream id 0x04, 0x03, 0x02, 0x01, - // fin - 0x01, // offset 0x54, 0x76, 0x10, 0x32, 0xDC, 0xFE, 0x98, 0xBA, @@ -1432,8 +1814,10 @@ TEST_P(QuicFramerTest, RevivedStreamFrame) { visitor_.stream_frames_[0]->offset); EXPECT_EQ("hello world!", visitor_.stream_frames_[0]->data); } +TEST_P(QuicFramerTest, StreamFrameInFecGroupVersion6) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_6); -TEST_P(QuicFramerTest, StreamFrameInFecGroup) { unsigned char packet[] = { // public flags (8 byte guid) 0x3C, @@ -1489,6 +1873,63 @@ TEST_P(QuicFramerTest, StreamFrameInFecGroup) { EXPECT_EQ("hello world!", visitor_.stream_frames_[0]->data); } +TEST_P(QuicFramerTest, StreamFrameInFecGroup) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_7); + + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x12, 0x34, + // private flags (fec group) + 0x02, + // first fec protected packet offset + 0x02, + + // frame type (stream frame with fin) + 0xFE, + // stream id + 0x04, 0x03, 0x02, 0x01, + // offset + 0x54, 0x76, 0x10, 0x32, + 0xDC, 0xFE, 0x98, 0xBA, + // data length + 0x0c, 0x00, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); + EXPECT_EQ(IN_FEC_GROUP, visitor_.header_->is_in_fec_group); + EXPECT_EQ(GG_UINT64_C(0x341256789ABA), + visitor_.header_->fec_group); + const size_t fec_offset = GetStartOfFecProtectedData( + PACKET_8BYTE_GUID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER); + EXPECT_EQ( + string(AsChars(packet) + fec_offset, arraysize(packet) - fec_offset), + visitor_.fec_protected_payload_); + + ASSERT_EQ(1u, visitor_.stream_frames_.size()); + EXPECT_EQ(0u, visitor_.ack_frames_.size()); + EXPECT_EQ(GG_UINT64_C(0x01020304), visitor_.stream_frames_[0]->stream_id); + EXPECT_TRUE(visitor_.stream_frames_[0]->fin); + EXPECT_EQ(GG_UINT64_C(0xBA98FEDC32107654), + visitor_.stream_frames_[0]->offset); + EXPECT_EQ("hello world!", visitor_.stream_frames_[0]->data); +} + TEST_P(QuicFramerTest, AckFrame) { unsigned char packet[] = { // public flags (8 byte guid) @@ -1503,7 +1944,7 @@ TEST_P(QuicFramerTest, AckFrame) { 0x00, // frame type (ack frame) - 0x02, + static_cast<unsigned char>((version_ == QUIC_VERSION_6) ? 0x02 : 0x01), // entropy hash of sent packets till least awaiting - 1. 0xAB, // least packet sequence number awaiting an ack @@ -1835,7 +2276,7 @@ TEST_P(QuicFramerTest, RstStreamFrame) { 0x00, // frame type (rst stream frame) - 0x04, + static_cast<unsigned char>((version_ == QUIC_VERSION_6) ? 0x04 : 0x27), // stream id 0x04, 0x03, 0x02, 0x01, // error code @@ -1864,9 +2305,9 @@ TEST_P(QuicFramerTest, RstStreamFrame) { // Now test framing boundaries for (size_t i = 2; i < 24; ++i) { string expected_error; - if (i < kQuicFrameTypeSize + kQuicStreamIdSize) { + if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize) { expected_error = "Unable to read stream_id."; - } else if (i < kQuicFrameTypeSize + kQuicStreamIdSize + + } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicErrorCodeSize) { expected_error = "Unable to read rst stream error code."; } else { @@ -1894,7 +2335,7 @@ TEST_P(QuicFramerTest, ConnectionCloseFrame) { 0x00, // frame type (connection close frame) - 0x05, + static_cast<unsigned char>((version_ == QUIC_VERSION_6) ? 0x05 : 0x2F), // error code 0x11, 0x00, 0x00, 0x00, @@ -1981,7 +2422,7 @@ TEST_P(QuicFramerTest, GoAwayFrame) { 0x00, // frame type (go away frame) - 0x06, + static_cast<unsigned char>((version_ == QUIC_VERSION_6) ? 0x06 : 0x37), // error code 0x09, 0x00, 0x00, 0x00, // stream id @@ -2015,7 +2456,7 @@ TEST_P(QuicFramerTest, GoAwayFrame) { if (i < kQuicFrameTypeSize + kQuicErrorCodeSize) { expected_error = "Unable to read go away error code."; } else if (i < kQuicFrameTypeSize + kQuicErrorCodeSize + - kQuicStreamIdSize) { + kQuicMaxStreamIdSize) { expected_error = "Unable to read last good stream id."; } else { expected_error = "Unable to read goaway reason."; @@ -2082,7 +2523,7 @@ TEST_P(QuicFramerTest, PublicResetPacket) { TEST_P(QuicFramerTest, VersionNegotiationPacket) { // Set a specific version. - framer_.set_version(QUIC_VERSION_6); + framer_.set_version(QUIC_VERSION_7); unsigned char packet[] = { // public flags (version, 8 byte guid) @@ -2091,7 +2532,7 @@ TEST_P(QuicFramerTest, VersionNegotiationPacket) { 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // version tag - 'Q', '0', '0', '6', + 'Q', '0', '0', '7', 'Q', '2', '.', '0', }; @@ -2102,7 +2543,7 @@ TEST_P(QuicFramerTest, VersionNegotiationPacket) { ASSERT_EQ(QUIC_NO_ERROR, framer_.error()); ASSERT_TRUE(visitor_.version_negotiation_packet_.get()); EXPECT_EQ(2u, visitor_.version_negotiation_packet_->versions.size()); - EXPECT_EQ(QUIC_VERSION_6, + EXPECT_EQ(QUIC_VERSION_7, visitor_.version_negotiation_packet_->versions[0]); for (size_t i = 0; i <= kPublicFlagsSize + PACKET_8BYTE_GUID; ++i) { @@ -2157,7 +2598,7 @@ TEST_P(QuicFramerTest, FecPacket) { EXPECT_EQ("abcdefghijklmnop", fec_data.redundancy); } -TEST_P(QuicFramerTest, ConstructPaddingFramePacket) { +TEST_P(QuicFramerTest, BuildPaddingFramePacket) { QuicPacketHeader header; header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; @@ -2185,7 +2626,7 @@ TEST_P(QuicFramerTest, ConstructPaddingFramePacket) { 0x00, // frame type (padding frame) - 0x00, + static_cast<unsigned char>((version_ == QUIC_VERSION_6) ? 0x00 : 0x07), }; uint64 header_size = @@ -2194,7 +2635,7 @@ TEST_P(QuicFramerTest, ConstructPaddingFramePacket) { memset(packet + header_size + 1, 0x00, kMaxPacketSize - header_size - 1); scoped_ptr<QuicPacket> data( - framer_.ConstructFrameDataPacket(header, frames).packet); + framer_.BuildUnsizedDataPacket(header, frames).packet); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -2202,7 +2643,7 @@ TEST_P(QuicFramerTest, ConstructPaddingFramePacket) { AsChars(packet), arraysize(packet)); } -TEST_P(QuicFramerTest, Construct4ByteSequenceNumberPaddingFramePacket) { +TEST_P(QuicFramerTest, Build4ByteSequenceNumberPaddingFramePacket) { QuicPacketHeader header; header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; @@ -2230,7 +2671,7 @@ TEST_P(QuicFramerTest, Construct4ByteSequenceNumberPaddingFramePacket) { 0x00, // frame type (padding frame) - 0x00, + static_cast<unsigned char>((version_ == QUIC_VERSION_6) ? 0x00 : 0x07), }; uint64 header_size = @@ -2239,7 +2680,7 @@ TEST_P(QuicFramerTest, Construct4ByteSequenceNumberPaddingFramePacket) { memset(packet + header_size + 1, 0x00, kMaxPacketSize - header_size - 1); scoped_ptr<QuicPacket> data( - framer_.ConstructFrameDataPacket(header, frames).packet); + framer_.BuildUnsizedDataPacket(header, frames).packet); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -2247,7 +2688,7 @@ TEST_P(QuicFramerTest, Construct4ByteSequenceNumberPaddingFramePacket) { AsChars(packet), arraysize(packet)); } -TEST_P(QuicFramerTest, Construct2ByteSequenceNumberPaddingFramePacket) { +TEST_P(QuicFramerTest, Build2ByteSequenceNumberPaddingFramePacket) { QuicPacketHeader header; header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; @@ -2275,7 +2716,7 @@ TEST_P(QuicFramerTest, Construct2ByteSequenceNumberPaddingFramePacket) { 0x00, // frame type (padding frame) - 0x00, + static_cast<unsigned char>((version_ == QUIC_VERSION_6) ? 0x00 : 0x07), }; uint64 header_size = @@ -2284,7 +2725,7 @@ TEST_P(QuicFramerTest, Construct2ByteSequenceNumberPaddingFramePacket) { memset(packet + header_size + 1, 0x00, kMaxPacketSize - header_size - 1); scoped_ptr<QuicPacket> data( - framer_.ConstructFrameDataPacket(header, frames).packet); + framer_.BuildUnsizedDataPacket(header, frames).packet); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -2292,7 +2733,7 @@ TEST_P(QuicFramerTest, Construct2ByteSequenceNumberPaddingFramePacket) { AsChars(packet), arraysize(packet)); } -TEST_P(QuicFramerTest, Construct1ByteSequenceNumberPaddingFramePacket) { +TEST_P(QuicFramerTest, Build1ByteSequenceNumberPaddingFramePacket) { QuicPacketHeader header; header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; @@ -2320,7 +2761,7 @@ TEST_P(QuicFramerTest, Construct1ByteSequenceNumberPaddingFramePacket) { 0x00, // frame type (padding frame) - 0x00, + static_cast<unsigned char>((version_ == QUIC_VERSION_6) ? 0x00 : 0x07), }; uint64 header_size = @@ -2329,7 +2770,7 @@ TEST_P(QuicFramerTest, Construct1ByteSequenceNumberPaddingFramePacket) { memset(packet + header_size + 1, 0x00, kMaxPacketSize - header_size - 1); scoped_ptr<QuicPacket> data( - framer_.ConstructFrameDataPacket(header, frames).packet); + framer_.BuildUnsizedDataPacket(header, frames).packet); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -2337,7 +2778,10 @@ TEST_P(QuicFramerTest, Construct1ByteSequenceNumberPaddingFramePacket) { AsChars(packet), arraysize(packet)); } -TEST_P(QuicFramerTest, ConstructStreamFramePacket) { +TEST_P(QuicFramerTest, BuildStreamFramePacketVersion6) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_6); + QuicPacketHeader header; header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; @@ -2386,7 +2830,63 @@ TEST_P(QuicFramerTest, ConstructStreamFramePacket) { }; scoped_ptr<QuicPacket> data( - framer_.ConstructFrameDataPacket(header, frames).packet); + framer_.BuildUnsizedDataPacket(header, frames).packet); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + +TEST_P(QuicFramerTest, BuildStreamFramePacket) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_7); + + QuicPacketHeader header; + header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.reset_flag = false; + header.public_header.version_flag = false; + header.fec_flag = false; + header.entropy_flag = true; + header.packet_sequence_number = GG_UINT64_C(0x77123456789ABC); + header.fec_group = 0; + + QuicStreamFrame stream_frame; + stream_frame.stream_id = 0x01020304; + stream_frame.fin = true; + stream_frame.offset = GG_UINT64_C(0xBA98FEDC32107654); + stream_frame.data = "hello world!"; + + QuicFrames frames; + frames.push_back(QuicFrame(&stream_frame)); + + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (entropy) + 0x01, + + // frame type (stream frame with fin and no length) + 0xBE, + // stream id + 0x04, 0x03, 0x02, 0x01, + // offset + 0x54, 0x76, 0x10, 0x32, + 0xDC, 0xFE, 0x98, 0xBA, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + scoped_ptr<QuicPacket> data( + framer_.BuildUnsizedDataPacket(header, frames).packet); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -2394,7 +2894,7 @@ TEST_P(QuicFramerTest, ConstructStreamFramePacket) { AsChars(packet), arraysize(packet)); } -TEST_P(QuicFramerTest, ConstructStreamFramePacketWithVersionFlag) { +TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) { QuicPacketHeader header; header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; @@ -2414,7 +2914,7 @@ TEST_P(QuicFramerTest, ConstructStreamFramePacketWithVersionFlag) { frames.push_back(QuicFrame(&stream_frame)); // Set a specific version. - framer_.set_version(QUIC_VERSION_6); + framer_.set_version(QUIC_VERSION_7); unsigned char packet[] = { // public flags (version, 8 byte guid) 0x3D, @@ -2422,24 +2922,20 @@ TEST_P(QuicFramerTest, ConstructStreamFramePacketWithVersionFlag) { 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // version tag - 'Q', '0', '0', '6', + 'Q', '0', '0', '7', // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, // private flags (entropy) 0x01, - // frame type (stream frame) - 0x01, + // frame type (stream frame with fin and no length) + 0xBE, // stream id 0x04, 0x03, 0x02, 0x01, - // fin - 0x01, // offset 0x54, 0x76, 0x10, 0x32, 0xDC, 0xFE, 0x98, 0xBA, - // data length - 0x0c, 0x00, // data 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', @@ -2448,7 +2944,7 @@ TEST_P(QuicFramerTest, ConstructStreamFramePacketWithVersionFlag) { QuicFramerPeer::SetIsServer(&framer_, false); scoped_ptr<QuicPacket> data( - framer_.ConstructFrameDataPacket(header, frames).packet); + framer_.BuildUnsizedDataPacket(header, frames).packet); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -2456,7 +2952,7 @@ TEST_P(QuicFramerTest, ConstructStreamFramePacketWithVersionFlag) { AsChars(packet), arraysize(packet)); } -TEST_P(QuicFramerTest, ConstructVersionNegotiationPacket) { +TEST_P(QuicFramerTest, BuildVersionNegotiationPacket) { QuicPacketPublicHeader header; header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.reset_flag = false; @@ -2470,21 +2966,21 @@ TEST_P(QuicFramerTest, ConstructVersionNegotiationPacket) { 0x98, 0xBA, 0xDC, 0xFE, // version tag 'Q', '0', '0', '6', - // 'Q', '0', '0', '7', + 'Q', '0', '0', '7', }; QuicVersionVector versions; versions.push_back(QUIC_VERSION_6); - // versions.push_back(QUIC_VERSION_7); + versions.push_back(QUIC_VERSION_7); scoped_ptr<QuicEncryptedPacket> data( - framer_.ConstructVersionNegotiationPacket(header, versions)); + framer_.BuildVersionNegotiationPacket(header, versions)); test::CompareCharArraysWithHexError("constructed packet", data->data(), data->length(), AsChars(packet), arraysize(packet)); } -TEST_P(QuicFramerTest, ConstructAckFramePacket) { +TEST_P(QuicFramerTest, BuildAckFramePacket) { QuicPacketHeader header; header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; @@ -2519,7 +3015,7 @@ TEST_P(QuicFramerTest, ConstructAckFramePacket) { 0x01, // frame type (ack frame) - 0x02, + static_cast<unsigned char>((version_ == QUIC_VERSION_6) ? 0x02 : 0x01), // entropy hash of sent packets till least awaiting - 1. 0x14, // least packet sequence number awaiting an ack @@ -2540,7 +3036,7 @@ TEST_P(QuicFramerTest, ConstructAckFramePacket) { }; scoped_ptr<QuicPacket> data( - framer_.ConstructFrameDataPacket(header, frames).packet); + framer_.BuildUnsizedDataPacket(header, frames).packet); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -2548,7 +3044,7 @@ TEST_P(QuicFramerTest, ConstructAckFramePacket) { AsChars(packet), arraysize(packet)); } -TEST_P(QuicFramerTest, ConstructCongestionFeedbackFramePacketTCP) { +TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTCP) { QuicPacketHeader header; header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; @@ -2589,7 +3085,7 @@ TEST_P(QuicFramerTest, ConstructCongestionFeedbackFramePacketTCP) { }; scoped_ptr<QuicPacket> data( - framer_.ConstructFrameDataPacket(header, frames).packet); + framer_.BuildUnsizedDataPacket(header, frames).packet); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -2597,7 +3093,7 @@ TEST_P(QuicFramerTest, ConstructCongestionFeedbackFramePacketTCP) { AsChars(packet), arraysize(packet)); } -TEST_P(QuicFramerTest, ConstructCongestionFeedbackFramePacketInterArrival) { +TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) { QuicPacketHeader header; header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; @@ -2662,7 +3158,7 @@ TEST_P(QuicFramerTest, ConstructCongestionFeedbackFramePacketInterArrival) { }; scoped_ptr<QuicPacket> data( - framer_.ConstructFrameDataPacket(header, frames).packet); + framer_.BuildUnsizedDataPacket(header, frames).packet); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -2670,7 +3166,7 @@ TEST_P(QuicFramerTest, ConstructCongestionFeedbackFramePacketInterArrival) { AsChars(packet), arraysize(packet)); } -TEST_P(QuicFramerTest, ConstructCongestionFeedbackFramePacketFixRate) { +TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketFixRate) { QuicPacketHeader header; header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; @@ -2709,7 +3205,7 @@ TEST_P(QuicFramerTest, ConstructCongestionFeedbackFramePacketFixRate) { }; scoped_ptr<QuicPacket> data( - framer_.ConstructFrameDataPacket(header, frames).packet); + framer_.BuildUnsizedDataPacket(header, frames).packet); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -2717,7 +3213,7 @@ TEST_P(QuicFramerTest, ConstructCongestionFeedbackFramePacketFixRate) { AsChars(packet), arraysize(packet)); } -TEST_P(QuicFramerTest, ConstructCongestionFeedbackFramePacketInvalidFeedback) { +TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInvalidFeedback) { QuicPacketHeader header; header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; @@ -2735,11 +3231,11 @@ TEST_P(QuicFramerTest, ConstructCongestionFeedbackFramePacketInvalidFeedback) { frames.push_back(QuicFrame(&congestion_feedback_frame)); scoped_ptr<QuicPacket> data( - framer_.ConstructFrameDataPacket(header, frames).packet); + framer_.BuildUnsizedDataPacket(header, frames).packet); ASSERT_TRUE(data == NULL); } -TEST_P(QuicFramerTest, ConstructRstFramePacket) { +TEST_P(QuicFramerTest, BuildRstFramePacket) { QuicPacketHeader header; header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; @@ -2767,7 +3263,7 @@ TEST_P(QuicFramerTest, ConstructRstFramePacket) { 0x00, // frame type (rst stream frame) - 0x04, + static_cast<unsigned char>((version_ == QUIC_VERSION_6) ? 0x04 : 0x27), // stream id 0x04, 0x03, 0x02, 0x01, // error code @@ -2785,7 +3281,7 @@ TEST_P(QuicFramerTest, ConstructRstFramePacket) { frames.push_back(QuicFrame(&rst_frame)); scoped_ptr<QuicPacket> data( - framer_.ConstructFrameDataPacket(header, frames).packet); + framer_.BuildUnsizedDataPacket(header, frames).packet); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -2793,7 +3289,7 @@ TEST_P(QuicFramerTest, ConstructRstFramePacket) { AsChars(packet), arraysize(packet)); } -TEST_P(QuicFramerTest, ConstructCloseFramePacket) { +TEST_P(QuicFramerTest, BuildCloseFramePacket) { QuicPacketHeader header; header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; @@ -2830,7 +3326,7 @@ TEST_P(QuicFramerTest, ConstructCloseFramePacket) { 0x01, // frame type (connection close frame) - 0x05, + static_cast<unsigned char>((version_ == QUIC_VERSION_6) ? 0x05 : 0x2F), // error code 0x08, 0x07, 0x06, 0x05, // error details length @@ -2862,7 +3358,7 @@ TEST_P(QuicFramerTest, ConstructCloseFramePacket) { }; scoped_ptr<QuicPacket> data( - framer_.ConstructFrameDataPacket(header, frames).packet); + framer_.BuildUnsizedDataPacket(header, frames).packet); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -2870,7 +3366,7 @@ TEST_P(QuicFramerTest, ConstructCloseFramePacket) { AsChars(packet), arraysize(packet)); } -TEST_P(QuicFramerTest, ConstructGoAwayPacket) { +TEST_P(QuicFramerTest, BuildGoAwayPacket) { QuicPacketHeader header; header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; @@ -2901,7 +3397,7 @@ TEST_P(QuicFramerTest, ConstructGoAwayPacket) { 0x01, // frame type (go away frame) - 0x06, + static_cast<unsigned char>((version_ == QUIC_VERSION_6) ? 0x06 : 0x37), // error code 0x08, 0x07, 0x06, 0x05, // stream id @@ -2916,7 +3412,7 @@ TEST_P(QuicFramerTest, ConstructGoAwayPacket) { }; scoped_ptr<QuicPacket> data( - framer_.ConstructFrameDataPacket(header, frames).packet); + framer_.BuildUnsizedDataPacket(header, frames).packet); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -2924,7 +3420,7 @@ TEST_P(QuicFramerTest, ConstructGoAwayPacket) { AsChars(packet), arraysize(packet)); } -TEST_P(QuicFramerTest, ConstructPublicResetPacket) { +TEST_P(QuicFramerTest, BuildPublicResetPacket) { QuicPublicResetPacket reset_packet; reset_packet.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); reset_packet.public_header.reset_flag = true; @@ -2947,7 +3443,7 @@ TEST_P(QuicFramerTest, ConstructPublicResetPacket) { }; scoped_ptr<QuicEncryptedPacket> data( - framer_.ConstructPublicResetPacket(reset_packet)); + framer_.BuildPublicResetPacket(reset_packet)); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -2955,7 +3451,7 @@ TEST_P(QuicFramerTest, ConstructPublicResetPacket) { AsChars(packet), arraysize(packet)); } -TEST_P(QuicFramerTest, ConstructFecPacket) { +TEST_P(QuicFramerTest, BuildFecPacket) { QuicPacketHeader header; header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; @@ -2992,7 +3488,7 @@ TEST_P(QuicFramerTest, ConstructFecPacket) { }; scoped_ptr<QuicPacket> data( - framer_.ConstructFecPacket(header, fec_data).packet); + framer_.BuildFecPacket(header, fec_data).packet); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -3120,7 +3616,7 @@ TEST_P(QuicFramerTest, DISABLED_Truncation) { frames.push_back(frame); scoped_ptr<QuicPacket> raw_ack_packet( - framer_.ConstructFrameDataPacket(header, frames).packet); + framer_.BuildUnsizedDataPacket(header, frames).packet); ASSERT_TRUE(raw_ack_packet != NULL); scoped_ptr<QuicEncryptedPacket> ack_packet( @@ -3134,7 +3630,7 @@ TEST_P(QuicFramerTest, DISABLED_Truncation) { frames.push_back(frame); scoped_ptr<QuicPacket> raw_close_packet( - framer_.ConstructFrameDataPacket(header, frames).packet); + framer_.BuildUnsizedDataPacket(header, frames).packet); ASSERT_TRUE(raw_close_packet != NULL); scoped_ptr<QuicEncryptedPacket> close_packet( @@ -3176,7 +3672,7 @@ TEST_P(QuicFramerTest, CleanTruncation) { frames.push_back(frame); scoped_ptr<QuicPacket> raw_ack_packet( - framer_.ConstructFrameDataPacket(header, frames).packet); + framer_.BuildUnsizedDataPacket(header, frames).packet); ASSERT_TRUE(raw_ack_packet != NULL); scoped_ptr<QuicEncryptedPacket> ack_packet( @@ -3190,7 +3686,7 @@ TEST_P(QuicFramerTest, CleanTruncation) { frames.push_back(frame); scoped_ptr<QuicPacket> raw_close_packet( - framer_.ConstructFrameDataPacket(header, frames).packet); + framer_.BuildUnsizedDataPacket(header, frames).packet); ASSERT_TRUE(raw_close_packet != NULL); scoped_ptr<QuicEncryptedPacket> close_packet( @@ -3212,7 +3708,7 @@ TEST_P(QuicFramerTest, CleanTruncation) { size_t original_raw_length = raw_ack_packet->length(); raw_ack_packet.reset( - framer_.ConstructFrameDataPacket(header, frames).packet); + framer_.BuildUnsizedDataPacket(header, frames).packet); ASSERT_TRUE(raw_ack_packet != NULL); EXPECT_EQ(original_raw_length, raw_ack_packet->length()); @@ -3223,12 +3719,15 @@ TEST_P(QuicFramerTest, CleanTruncation) { original_raw_length = raw_close_packet->length(); raw_close_packet.reset( - framer_.ConstructFrameDataPacket(header, frames).packet); + framer_.BuildUnsizedDataPacket(header, frames).packet); ASSERT_TRUE(raw_ack_packet != NULL); EXPECT_EQ(original_raw_length, raw_close_packet->length()); } -TEST_P(QuicFramerTest, EntropyFlagTest) { +TEST_P(QuicFramerTest, EntropyFlagTestVersion6) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_6); + unsigned char packet[] = { // public flags (8 byte guid) 0x3C, @@ -3267,7 +3766,48 @@ TEST_P(QuicFramerTest, EntropyFlagTest) { EXPECT_FALSE(visitor_.header_->fec_flag); }; -TEST_P(QuicFramerTest, FecEntropyTest) { +TEST_P(QuicFramerTest, EntropyFlagTest) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_7); + + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (Entropy) + 0x01, + + // frame type (stream frame with fin and no length) + 0xBE, + // stream id + 0x04, 0x03, 0x02, 0x01, + // offset + 0x54, 0x76, 0x10, 0x32, + 0xDC, 0xFE, 0x98, 0xBA, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(visitor_.header_->entropy_flag); + EXPECT_EQ(1 << 4, visitor_.header_->entropy_hash); + EXPECT_FALSE(visitor_.header_->fec_flag); +}; + +TEST_P(QuicFramerTest, FecEntropyTestVersion6) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_6); + unsigned char packet[] = { // public flags (8 byte guid) 0x3C, @@ -3308,7 +3848,50 @@ TEST_P(QuicFramerTest, FecEntropyTest) { EXPECT_EQ(1 << 4, visitor_.header_->entropy_hash); }; -TEST_P(QuicFramerTest, StopPacketProcessing) { +TEST_P(QuicFramerTest, FecEntropyTest) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_7); + + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (Entropy & fec group & FEC) + 0x07, + // first fec protected packet offset + 0xFF, + + // frame type (stream frame with fin and no length) + 0xBE, + // stream id + 0x04, 0x03, 0x02, 0x01, + // offset + 0x54, 0x76, 0x10, 0x32, + 0xDC, 0xFE, 0x98, 0xBA, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(visitor_.header_->fec_flag); + EXPECT_TRUE(visitor_.header_->entropy_flag); + EXPECT_EQ(1 << 4, visitor_.header_->entropy_hash); +}; + +TEST_P(QuicFramerTest, StopPacketProcessingVersion6) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_6); + unsigned char packet[] = { // public flags (8 byte guid) 0x3C, @@ -3369,6 +3952,68 @@ TEST_P(QuicFramerTest, StopPacketProcessing) { EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); } +TEST_P(QuicFramerTest, StopPacketProcessing) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_7); + + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // Entropy + 0x01, + + // frame type (stream frame with fin) + 0xFE, + // stream id + 0x04, 0x03, 0x02, 0x01, + // offset + 0x54, 0x76, 0x10, 0x32, + 0xDC, 0xFE, 0x98, 0xBA, + // data length + 0x0c, 0x00, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + + // frame type (ack frame) + 0x02, + // entropy hash of sent packets till least awaiting - 1. + 0x14, + // least packet sequence number awaiting an ack + 0xA0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // entropy hash of all received packets. + 0x43, + // largest observed packet sequence number + 0xBF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // num missing packets + 0x01, + // missing packet + 0xBE, 0x9A, 0x78, 0x56, + 0x34, 0x12, + }; + + MockFramerVisitor visitor; + framer_.set_visitor(&visitor); + EXPECT_CALL(visitor, OnPacket()); + EXPECT_CALL(visitor, OnPacketHeader(_)); + EXPECT_CALL(visitor, OnStreamFrame(_)).WillOnce(Return(false)); + EXPECT_CALL(visitor, OnAckFrame(_)).Times(0); + EXPECT_CALL(visitor, OnPacketComplete()); + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); +} + TEST_P(QuicFramerTest, ConnectionCloseWithInvalidAck) { unsigned char packet[] = { // public flags (8 byte guid) @@ -3383,7 +4028,7 @@ TEST_P(QuicFramerTest, ConnectionCloseWithInvalidAck) { 0x00, // frame type (connection close frame) - 0x05, + static_cast<unsigned char>((version_ == QUIC_VERSION_6) ? 0x05 : 0x2F), // error code 0x11, 0x00, 0x00, 0x00, // error details length diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc index 9f3a69c..997dc95 100644 --- a/net/quic/quic_http_stream_test.cc +++ b/net/quic/quic_http_stream_test.cc @@ -288,7 +288,7 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> { QuicFrames frames; frames.push_back(frame); scoped_ptr<QuicPacket> packet( - framer_.ConstructFrameDataPacket(header_, frames).packet); + framer_.BuildUnsizedDataPacket(header_, frames).packet); return framer_.EncryptPacket( ENCRYPTION_NONE, header.packet_sequence_number, *packet); } diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc index 8ea2d10..0722b58 100644 --- a/net/quic/quic_network_transaction_unittest.cc +++ b/net/quic/quic_network_transaction_unittest.cc @@ -146,7 +146,7 @@ class QuicNetworkTransactionTest : public PlatformTest { frames.push_back(QuicFrame(&ack)); frames.push_back(QuicFrame(&feedback)); scoped_ptr<QuicPacket> packet( - framer.ConstructFrameDataPacket(header, frames).packet); + framer.BuildUnsizedDataPacket(header, frames).packet); return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket( ENCRYPTION_NONE, header.packet_sequence_number, *packet)); } @@ -197,7 +197,7 @@ class QuicNetworkTransactionTest : public PlatformTest { QuicFrames frames; frames.push_back(frame); scoped_ptr<QuicPacket> packet( - framer.ConstructFrameDataPacket(header, frames).packet); + framer.BuildUnsizedDataPacket(header, frames).packet); return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket( ENCRYPTION_NONE, header.packet_sequence_number, *packet)); } diff --git a/net/quic/quic_packet_creator.cc b/net/quic/quic_packet_creator.cc index eec8ab3..19e58c3 100644 --- a/net/quic/quic_packet_creator.cc +++ b/net/quic/quic_packet_creator.cc @@ -77,12 +77,15 @@ void QuicPacketCreator::StopSendingVersion() { } } -bool QuicPacketCreator::HasRoomForStreamFrame() const { - return BytesFree() > QuicFramer::GetMinStreamFrameSize(); +bool QuicPacketCreator::HasRoomForStreamFrame(QuicStreamId id, + QuicStreamOffset offset) const { + return BytesFree() > + QuicFramer::GetMinStreamFrameSize(framer_->version(), id, offset, true); } // static size_t QuicPacketCreator::StreamFramePacketOverhead( + QuicVersion version, QuicGuidLength guid_length, bool include_version, QuicSequenceNumberLength sequence_number_length, @@ -90,7 +93,7 @@ size_t QuicPacketCreator::StreamFramePacketOverhead( return GetPacketHeaderSize(guid_length, include_version, sequence_number_length, is_in_fec_group) + // Assumes this is a stream with a single lone packet. - QuicFramer::GetMinStreamFrameSize(1, 0, true); + QuicFramer::GetMinStreamFrameSize(version, 1u, 0u, true); } size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id, @@ -100,17 +103,17 @@ size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id, QuicFrame* frame) { DCHECK_GT(options_.max_packet_length, StreamFramePacketOverhead( - PACKET_8BYTE_GUID, kIncludeVersion, + framer_->version(), PACKET_8BYTE_GUID, kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER, IN_FEC_GROUP)); - DCHECK(HasRoomForStreamFrame()); + DCHECK(HasRoomForStreamFrame(id, offset)); const size_t free_bytes = BytesFree(); size_t bytes_consumed = 0; if (data.size() != 0) { size_t min_last_stream_frame_size = - QuicFramer::GetMinStreamFrameSize(id, offset, true); - // Comparing against the last stream frame size including the length + QuicFramer::GetMinStreamFrameSize(framer_->version(), id, offset, true); + // Comparing against the last stream frame size including the frame length // guarantees that all the bytes will fit. Otherwise there is a // discontinuity where the packet goes one byte over due to the length data. if (data.size() + min_last_stream_frame_size + kQuicStreamPayloadLengthSize @@ -119,6 +122,7 @@ size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id, bytes_consumed = min<size_t>(free_bytes - min_last_stream_frame_size, data.size()); } else { + DCHECK_LT(data.size(), BytesFree()); bytes_consumed = data.size(); } @@ -171,7 +175,7 @@ SerializedPacket QuicPacketCreator::SerializePacket() { QuicPacketHeader header; FillPacketHeader(fec_group_number_, false, false, &header); - SerializedPacket serialized = framer_->ConstructFrameDataPacket( + SerializedPacket serialized = framer_->BuildDataPacket( header, queued_frames_, packet_size_); queued_frames_.clear(); packet_size_ = GetPacketHeaderSize(options_.send_guid_length, @@ -192,7 +196,7 @@ SerializedPacket QuicPacketCreator::SerializeFec() { QuicFecData fec_data; fec_data.fec_group = fec_group_->min_protected_packet(); fec_data.redundancy = fec_group_->payload_parity(); - SerializedPacket serialized = framer_->ConstructFecPacket(header, fec_data); + SerializedPacket serialized = framer_->BuildFecPacket(header, fec_data); fec_group_.reset(NULL); fec_group_number_ = 0; // Reset packet_size_, since the next packet may not have an FEC group. @@ -221,7 +225,7 @@ QuicEncryptedPacket* QuicPacketCreator::SerializeVersionNegotiationPacket( header.version_flag = true; header.versions = supported_versions; QuicEncryptedPacket* encrypted = - framer_->ConstructVersionNegotiationPacket(header, supported_versions); + framer_->BuildVersionNegotiationPacket(header, supported_versions); DCHECK(encrypted); DCHECK_GE(options_.max_packet_length, encrypted->length()); return encrypted; diff --git a/net/quic/quic_packet_creator.h b/net/quic/quic_packet_creator.h index 6c21a94..a1d74fa 100644 --- a/net/quic/quic_packet_creator.h +++ b/net/quic/quic_packet_creator.h @@ -71,12 +71,13 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { // The overhead the framing will add for a packet with one frame. static size_t StreamFramePacketOverhead( + QuicVersion version, QuicGuidLength guid_length, bool include_version, QuicSequenceNumberLength sequence_number_length, InFecGroup is_in_fec_group); - bool HasRoomForStreamFrame() const; + bool HasRoomForStreamFrame(QuicStreamId id, QuicStreamOffset offset) const; // Converts a raw payload to a frame which fits into the currently open // packet if there is one. Returns the number of bytes consumed from data. diff --git a/net/quic/quic_packet_creator_test.cc b/net/quic/quic_packet_creator_test.cc index b55f350..1d793d2 100644 --- a/net/quic/quic_packet_creator_test.cc +++ b/net/quic/quic_packet_creator_test.cc @@ -177,6 +177,35 @@ TEST_F(QuicPacketCreatorTest, CreateStreamFrameFinOnly) { delete frame.stream_frame; } +TEST_F(QuicPacketCreatorTest, CreateAllFreeBytesForStreamFrames) { + QuicStreamId kStreamId = 1u; + QuicStreamOffset kOffset = 1u; + for (int i = 0; i < 100; ++i) { + creator_.options()->max_packet_length = i; + const size_t max_plaintext_size = client_framer_.GetMaxPlaintextSize(i); + const bool should_have_room = max_plaintext_size > + (QuicFramer::GetMinStreamFrameSize( + client_framer_.version(), kStreamId, kOffset, true) + + GetPacketHeaderSize(creator_.options()->send_guid_length, + kIncludeVersion, + creator_.options()->send_sequence_number_length, + NOT_IN_FEC_GROUP)); + ASSERT_EQ(should_have_room, + creator_.HasRoomForStreamFrame(kStreamId, kOffset)); + if (should_have_room) { + QuicFrame frame; + size_t bytes_consumed = creator_.CreateStreamFrame( + kStreamId, "testdata", kOffset, false, &frame); + EXPECT_LT(0u, bytes_consumed); + ASSERT_TRUE(creator_.AddSavedFrame(frame)); + SerializedPacket serialized_packet = creator_.SerializePacket(); + ASSERT_TRUE(serialized_packet.packet); + delete serialized_packet.packet; + delete serialized_packet.retransmittable_frames; + } + } +} + TEST_F(QuicPacketCreatorTest, SerializeVersionNegotiationPacket) { QuicPacketCreatorPeer::SetIsServer(&creator_, true); QuicVersionVector versions; @@ -226,6 +255,7 @@ TEST_P(QuicPacketCreatorTest, CreateStreamFrameTooLarge) { // A string larger than fits into a frame. size_t payload_length; creator_.options()->max_packet_length = GetPacketLengthForOneStream( + client_framer_.version(), QuicPacketCreatorPeer::SendVersionInPacket(&creator_), NOT_IN_FEC_GROUP, &payload_length); QuicFrame frame; diff --git a/net/quic/quic_packet_generator.cc b/net/quic/quic_packet_generator.cc index 34ed0a0..7600010 100644 --- a/net/quic/quic_packet_generator.cc +++ b/net/quic/quic_packet_generator.cc @@ -89,7 +89,7 @@ QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id, DCHECK(data.empty() || packet_creator_->BytesFree() == 0u); // TODO(ianswett): Restore packet reordering. - if (should_flush_ || !packet_creator_->HasRoomForStreamFrame()) { + if (should_flush_ || !packet_creator_->HasRoomForStreamFrame(id, offset)) { SerializeAndSendPacket(); } diff --git a/net/quic/quic_protocol.cc b/net/quic/quic_protocol.cc index abbbadf..6d21710 100644 --- a/net/quic/quic_protocol.cc +++ b/net/quic/quic_protocol.cc @@ -127,8 +127,8 @@ QuicTag QuicVersionToQuicTag(const QuicVersion version) { switch (version) { case QUIC_VERSION_6: return MakeQuicTag('Q', '0', '0', '6'); - // case QUIC_VERSION_7 - // return MakeQuicTag('Q', '0', '0', '7'); + case QUIC_VERSION_7: + return MakeQuicTag('Q', '0', '0', '7'); default: // This shold be an ERROR because we should never attempt to convert an // invalid QuicVersion to be written to the wire. @@ -139,12 +139,12 @@ QuicTag QuicVersionToQuicTag(const QuicVersion version) { QuicVersion QuicTagToQuicVersion(const QuicTag version_tag) { const QuicTag quic_tag_v6 = MakeQuicTag('Q', '0', '0', '6'); - // const QuicTag quic_tag_v7 = MakeQuicTag('Q', '0', '0', '7'); + const QuicTag quic_tag_v7 = MakeQuicTag('Q', '0', '0', '7'); if (version_tag == quic_tag_v6) { return QUIC_VERSION_6; - // } else if (version_tag == quic_tag_v7) { - // return QUIC_VERSION_7; + } else if (version_tag == quic_tag_v7) { + return QUIC_VERSION_7; } else { // Reading from the client so this should not be considered an ERROR. DLOG(INFO) << "Unsupported QuicTag version: " @@ -159,8 +159,8 @@ string QuicVersionToString(const QuicVersion version) { switch (version) { case QUIC_VERSION_6: return "QUIC_VERSION_6"; - // case QUIC_VERSION_7: - // return "QUIC_VERSION_7"; + case QUIC_VERSION_7: + return "QUIC_VERSION_7"; default: return "QUIC_VERSION_UNSUPPORTED"; } diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h index 8b17cd5..fe4da4c 100644 --- a/net/quic/quic_protocol.h +++ b/net/quic/quic_protocol.h @@ -186,14 +186,16 @@ enum QuicVersion { // Special case to indicate unknown/unsupported QUIC version. QUIC_VERSION_UNSUPPORTED = 0, - QUIC_VERSION_6 = 6, // Current version. + QUIC_VERSION_6 = 6, + QUIC_VERSION_7 = 7, // Current version. }; // This vector contains QUIC versions which we currently support. // This should be ordered such that the highest supported version is the first // element, with subsequent elements in descending order (versions can be // skipped as necessary). -static const QuicVersion kSupportedQuicVersions[] = {QUIC_VERSION_6}; +static const QuicVersion kSupportedQuicVersions[] = + {QUIC_VERSION_7, QUIC_VERSION_6}; typedef std::vector<QuicVersion> QuicVersionVector; diff --git a/net/quic/quic_protocol_test.cc b/net/quic/quic_protocol_test.cc index a22cb62..877f03d 100644 --- a/net/quic/quic_protocol_test.cc +++ b/net/quic/quic_protocol_test.cc @@ -135,10 +135,10 @@ TEST(QuicProtocolTest, QuicVersionToString) { QuicVersion single_version[] = {QUIC_VERSION_6}; EXPECT_EQ("QUIC_VERSION_6,", QuicVersionArrayToString(single_version, arraysize(single_version))); - // QuicVersion multiple_versions[] = {QUIC_VERSION_7, QUIC_VERSION_6}; - // EXPECT_EQ("QUIC_VERSION_7,QUIC_VERSION_6,", - // QuicVersionArrayToString(multiple_versions, - // arraysize(multiple_versions))); + QuicVersion multiple_versions[] = {QUIC_VERSION_7, QUIC_VERSION_6}; + EXPECT_EQ("QUIC_VERSION_7,QUIC_VERSION_6,", + QuicVersionArrayToString(multiple_versions, + arraysize(multiple_versions))); } } // namespace diff --git a/net/quic/quic_received_entropy_manager.cc b/net/quic/quic_received_entropy_manager.cc deleted file mode 100644 index 57020eb..0000000 --- a/net/quic/quic_received_entropy_manager.cc +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/quic/quic_received_entropy_manager.h" - -#include "base/logging.h" -#include "net/base/linked_hash_map.h" - -using std::make_pair; -using std::max; -using std::min; - -namespace net { - -QuicReceivedEntropyManager::QuicReceivedEntropyManager() - : packets_entropy_hash_(0), - largest_sequence_number_(0) {} - -QuicReceivedEntropyManager::~QuicReceivedEntropyManager() {} - -QuicPacketSequenceNumber -QuicReceivedEntropyManager::LargestSequenceNumber() const { - if (packets_entropy_.empty()) { - return 0; - } - return packets_entropy_.rbegin()->first; -} - -void QuicReceivedEntropyManager::RecordPacketEntropyHash( - QuicPacketSequenceNumber sequence_number, - QuicPacketEntropyHash entropy_hash) { - if (sequence_number < largest_sequence_number_) { - DLOG(INFO) << "Ignoring received packet entropy for sequence_number:" - << sequence_number << " less than largest_peer_sequence_number:" - << largest_sequence_number_; - return; - } - packets_entropy_.insert(make_pair(sequence_number, entropy_hash)); - packets_entropy_hash_ ^= entropy_hash; - DVLOG(2) << "setting cumulative received entropy hash to: " - << static_cast<int>(packets_entropy_hash_) - << " updated with sequence number " << sequence_number - << " entropy hash: " << static_cast<int>(entropy_hash); -} - -QuicPacketEntropyHash QuicReceivedEntropyManager::EntropyHash( - QuicPacketSequenceNumber sequence_number) const { - DCHECK_LE(sequence_number, LargestSequenceNumber()); - DCHECK_GE(sequence_number, largest_sequence_number_); - if (sequence_number == LargestSequenceNumber()) { - return packets_entropy_hash_; - } - - ReceivedEntropyMap::const_iterator it = - packets_entropy_.upper_bound(sequence_number); - // When this map is empty we should only query entropy for - // |largest_received_sequence_number_|. - LOG_IF(WARNING, it != packets_entropy_.end()) - << "largest_received: " << LargestSequenceNumber() - << " sequence_number: " << sequence_number; - - // TODO(satyamshekhar): Make this O(1). - QuicPacketEntropyHash hash = packets_entropy_hash_; - for (; it != packets_entropy_.end(); ++it) { - hash ^= it->second; - } - return hash; -} - -void QuicReceivedEntropyManager::RecalculateEntropyHash( - QuicPacketSequenceNumber peer_least_unacked, - QuicPacketEntropyHash entropy_hash) { - DLOG_IF(WARNING, peer_least_unacked > LargestSequenceNumber()) - << "Prematurely updating the entropy manager before registering the " - << "entropy of the containing packet creates a temporary inconsistency."; - if (peer_least_unacked < largest_sequence_number_) { - DLOG(INFO) << "Ignoring received peer_least_unacked:" << peer_least_unacked - << " less than largest_peer_sequence_number:" - << largest_sequence_number_; - return; - } - largest_sequence_number_ = peer_least_unacked; - packets_entropy_hash_ = entropy_hash; - ReceivedEntropyMap::iterator it = - packets_entropy_.lower_bound(peer_least_unacked); - // TODO(satyamshekhar): Make this O(1). - for (; it != packets_entropy_.end(); ++it) { - packets_entropy_hash_ ^= it->second; - } - // Discard entropies before least unacked. - packets_entropy_.erase( - packets_entropy_.begin(), - packets_entropy_.lower_bound( - min(peer_least_unacked, LargestSequenceNumber()))); -} - -} // namespace net diff --git a/net/quic/quic_received_entropy_manager.h b/net/quic/quic_received_entropy_manager.h deleted file mode 100644 index d969834..0000000 --- a/net/quic/quic_received_entropy_manager.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// Manages the packet entropy calculation for both sent and received packets -// for a connection. - -#ifndef NET_QUIC_QUIC_RECEIVED_ENTROPY_MANAGER_H_ -#define NET_QUIC_QUIC_RECEIVED_ENTROPY_MANAGER_H_ - -#include "net/quic/quic_framer.h" -#include "net/quic/quic_protocol.h" - -namespace net { - -// Records all received packets by a connection to track the cumulative -// entropy of received packets. Also, called by the framer when it truncates -// an ack frame to calculate the correct entropy value for the ack frame being -// serialized. -class NET_EXPORT_PRIVATE QuicReceivedEntropyManager : - public QuicReceivedEntropyHashCalculatorInterface { - public: - QuicReceivedEntropyManager(); - virtual ~QuicReceivedEntropyManager(); - - // Record the received entropy hash against |sequence_number|. - void RecordPacketEntropyHash(QuicPacketSequenceNumber sequence_number, - QuicPacketEntropyHash entropy_hash); - - // QuicReceivedEntropyHashCalculatorInterface - // Called by QuicFramer, when the outgoing ack gets truncated, to recalculate - // the received entropy hash for the truncated ack frame. - virtual QuicPacketEntropyHash EntropyHash( - QuicPacketSequenceNumber sequence_number) const OVERRIDE; - - QuicPacketSequenceNumber LargestSequenceNumber() const; - - // Recalculate the entropy hash and clears old packet entropies, - // now that the sender sent us the |entropy_hash| for packets up to, - // but not including, |peer_least_unacked|. - void RecalculateEntropyHash(QuicPacketSequenceNumber peer_least_unacked, - QuicPacketEntropyHash entropy_hash); - - QuicPacketEntropyHash packets_entropy_hash() const { - return packets_entropy_hash_; - } - - private: - typedef std::map<QuicPacketSequenceNumber, - QuicPacketEntropyHash> ReceivedEntropyMap; - - // TODO(satyamshekhar): Can be optimized using an interval set like data - // structure. - // Map of received sequence numbers to their corresponding entropy. - // Every received packet has an entry, and packets without the entropy bit set - // have an entropy value of 0. - // TODO(ianswett): When the entropy flag is off, the entropy should not be 0. - ReceivedEntropyMap packets_entropy_; - - // Cumulative hash of entropy of all received packets. - QuicPacketEntropyHash packets_entropy_hash_; - - // The largest sequence number cleared by RecalculateEntropyHash. - // Received entropy cannot be calculated for numbers less than it. - QuicPacketSequenceNumber largest_sequence_number_; -}; - -} // namespace net - -#endif // NET_QUIC_QUIC_RECEIVED_ENTROPY_MANAGER_H_ diff --git a/net/quic/quic_received_entropy_manager_test.cc b/net/quic/quic_received_entropy_manager_test.cc deleted file mode 100644 index c857315e..0000000 --- a/net/quic/quic_received_entropy_manager_test.cc +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/quic/quic_received_entropy_manager.h" - -#include <algorithm> -#include <vector> - -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using std::make_pair; -using std::pair; -using std::vector; - -namespace net { -namespace test { -namespace { - -class QuicReceivedEntropyManagerTest : public ::testing::Test { - protected: - QuicReceivedEntropyManager entropy_manager_; -}; - -TEST_F(QuicReceivedEntropyManagerTest, ReceivedPacketEntropyHash) { - vector<pair<QuicPacketSequenceNumber, QuicPacketEntropyHash> > entropies; - entropies.push_back(make_pair(1, 12)); - entropies.push_back(make_pair(7, 1)); - entropies.push_back(make_pair(2, 33)); - entropies.push_back(make_pair(5, 3)); - entropies.push_back(make_pair(8, 34)); - - for (size_t i = 0; i < entropies.size(); ++i) { - entropy_manager_.RecordPacketEntropyHash(entropies[i].first, - entropies[i].second); - } - - sort(entropies.begin(), entropies.end()); - - QuicPacketEntropyHash hash = 0; - size_t index = 0; - for (size_t i = 1; i <= (*entropies.rbegin()).first; ++i) { - if (entropies[index].first == i) { - hash ^= entropies[index].second; - ++index; - } - EXPECT_EQ(hash, entropy_manager_.EntropyHash(i)); - } -} - -TEST_F(QuicReceivedEntropyManagerTest, EntropyHashBelowLeastObserved) { - EXPECT_EQ(0, entropy_manager_.EntropyHash(0)); - entropy_manager_.RecordPacketEntropyHash(4, 5); - EXPECT_EQ(0, entropy_manager_.EntropyHash(3)); -} - -TEST_F(QuicReceivedEntropyManagerTest, EntropyHashAboveLargestObserved) { - EXPECT_EQ(0, entropy_manager_.EntropyHash(0)); - entropy_manager_.RecordPacketEntropyHash(4, 5); - EXPECT_EQ(0, entropy_manager_.EntropyHash(3)); -} - -TEST_F(QuicReceivedEntropyManagerTest, RecalculateEntropyHash) { - vector<pair<QuicPacketSequenceNumber, QuicPacketEntropyHash> > entropies; - entropies.push_back(make_pair(1, 12)); - entropies.push_back(make_pair(2, 1)); - entropies.push_back(make_pair(3, 33)); - entropies.push_back(make_pair(4, 3)); - entropies.push_back(make_pair(5, 34)); - entropies.push_back(make_pair(6, 29)); - - QuicPacketEntropyHash entropy_hash = 0; - for (size_t i = 0; i < entropies.size(); ++i) { - entropy_manager_.RecordPacketEntropyHash(entropies[i].first, - entropies[i].second); - entropy_hash ^= entropies[i].second; - } - EXPECT_EQ(entropy_hash, entropy_manager_.EntropyHash(6)); - - // Now set the entropy hash up to 4 to be 100. - entropy_hash ^= 100; - for (size_t i = 0; i < 3; ++i) { - entropy_hash ^= entropies[i].second; - } - entropy_manager_.RecalculateEntropyHash(4, 100); - EXPECT_EQ(entropy_hash, entropy_manager_.EntropyHash(6)); - - // Ensure it doesn't change with an old received sequence number or entropy. - entropy_manager_.RecordPacketEntropyHash(1, 50); - EXPECT_EQ(entropy_hash, entropy_manager_.EntropyHash(6)); - - entropy_manager_.RecalculateEntropyHash(1, 50); - EXPECT_EQ(entropy_hash, entropy_manager_.EntropyHash(6)); -} - -} // namespace -} // namespace test -} // namespace net diff --git a/net/quic/quic_received_packet_manager.cc b/net/quic/quic_received_packet_manager.cc index e69de29..f6f94cc 100644 --- a/net/quic/quic_received_packet_manager.cc +++ b/net/quic/quic_received_packet_manager.cc @@ -0,0 +1,188 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/quic_received_packet_manager.h" + +#include "base/logging.h" +#include "net/base/linked_hash_map.h" + +using std::make_pair; +using std::max; +using std::min; + +namespace net { + +QuicReceivedPacketManager::QuicReceivedPacketManager() + : packets_entropy_hash_(0), + largest_sequence_number_(0), + peer_largest_observed_packet_(0), + least_packet_awaited_by_peer_(1), + peer_least_packet_awaiting_ack_(0), + time_largest_observed_(QuicTime::Zero()) { + received_info_.largest_observed = 0; + received_info_.entropy_hash = 0; +} + +QuicReceivedPacketManager::~QuicReceivedPacketManager() {} + +void QuicReceivedPacketManager::RecordPacketReceived( + const QuicPacketHeader& header, QuicTime receipt_time) { + QuicPacketSequenceNumber sequence_number = header.packet_sequence_number; + DCHECK(IsAwaitingPacket(sequence_number)); + + InsertMissingPacketsBetween( + &received_info_, + max(received_info_.largest_observed + 1, peer_least_packet_awaiting_ack_), + header.packet_sequence_number); + + if (received_info_.largest_observed > header.packet_sequence_number) { + // We've gotten one of the out of order packets - remove it from our + // "missing packets" list. + DVLOG(1) << "Removing " << sequence_number << " from missing list"; + received_info_.missing_packets.erase(sequence_number); + } + if (header.packet_sequence_number > received_info_.largest_observed) { + received_info_.largest_observed = header.packet_sequence_number; + time_largest_observed_ = receipt_time; + } + RecordPacketEntropyHash(sequence_number, header.entropy_hash); +} + +bool QuicReceivedPacketManager::IsAwaitingPacket( + QuicPacketSequenceNumber sequence_number) { + return ::net::IsAwaitingPacket(received_info_, sequence_number); +} + +void QuicReceivedPacketManager::UpdateReceivedPacketInfo( + ReceivedPacketInfo* received_info, QuicTime approximate_now) { + *received_info = received_info_; + received_info->entropy_hash = EntropyHash(received_info_.largest_observed); + if (time_largest_observed_ == QuicTime::Zero()) { + // We have not received any new higher sequence numbers since we sent our + // last ACK. + received_info->delta_time_largest_observed = QuicTime::Delta::Infinite(); + } else { + received_info->delta_time_largest_observed = + approximate_now.Subtract(time_largest_observed_); + + time_largest_observed_ = QuicTime::Zero(); + } +} + +void QuicReceivedPacketManager::RecordPacketEntropyHash( + QuicPacketSequenceNumber sequence_number, + QuicPacketEntropyHash entropy_hash) { + if (sequence_number < largest_sequence_number_) { + DLOG(INFO) << "Ignoring received packet entropy for sequence_number:" + << sequence_number << " less than largest_peer_sequence_number:" + << largest_sequence_number_; + return; + } + packets_entropy_.insert(make_pair(sequence_number, entropy_hash)); + packets_entropy_hash_ ^= entropy_hash; + DVLOG(2) << "setting cumulative received entropy hash to: " + << static_cast<int>(packets_entropy_hash_) + << " updated with sequence number " << sequence_number + << " entropy hash: " << static_cast<int>(entropy_hash); +} + +QuicPacketEntropyHash QuicReceivedPacketManager::EntropyHash( + QuicPacketSequenceNumber sequence_number) const { + DCHECK_LE(sequence_number, received_info_.largest_observed); + DCHECK_GE(sequence_number, largest_sequence_number_); + if (sequence_number == received_info_.largest_observed) { + return packets_entropy_hash_; + } + + ReceivedEntropyMap::const_iterator it = + packets_entropy_.upper_bound(sequence_number); + // When this map is empty we should only query entropy for + // |largest_received_sequence_number_|. + LOG_IF(WARNING, it != packets_entropy_.end()) + << "largest_received: " << received_info_.largest_observed + << " sequence_number: " << sequence_number; + + // TODO(satyamshekhar): Make this O(1). + QuicPacketEntropyHash hash = packets_entropy_hash_; + for (; it != packets_entropy_.end(); ++it) { + hash ^= it->second; + } + return hash; +} + +void QuicReceivedPacketManager::RecalculateEntropyHash( + QuicPacketSequenceNumber peer_least_unacked, + QuicPacketEntropyHash entropy_hash) { + DLOG_IF(WARNING, peer_least_unacked > received_info_.largest_observed) + << "Prematurely updating the entropy manager before registering the " + << "entropy of the containing packet creates a temporary inconsistency."; + if (peer_least_unacked < largest_sequence_number_) { + DLOG(INFO) << "Ignoring received peer_least_unacked:" << peer_least_unacked + << " less than largest_peer_sequence_number:" + << largest_sequence_number_; + return; + } + largest_sequence_number_ = peer_least_unacked; + packets_entropy_hash_ = entropy_hash; + ReceivedEntropyMap::iterator it = + packets_entropy_.lower_bound(peer_least_unacked); + // TODO(satyamshekhar): Make this O(1). + for (; it != packets_entropy_.end(); ++it) { + packets_entropy_hash_ ^= it->second; + } + // Discard entropies before least unacked. + packets_entropy_.erase( + packets_entropy_.begin(), + packets_entropy_.lower_bound( + min(peer_least_unacked, received_info_.largest_observed))); +} + +void QuicReceivedPacketManager::UpdatePacketInformationReceivedByPeer( + const QuicAckFrame& incoming_ack) { + // ValidateAck should fail if largest_observed ever shrinks. + DCHECK_LE(peer_largest_observed_packet_, + incoming_ack.received_info.largest_observed); + peer_largest_observed_packet_ = incoming_ack.received_info.largest_observed; + + if (incoming_ack.received_info.missing_packets.empty()) { + least_packet_awaited_by_peer_ = peer_largest_observed_packet_ + 1; + } else { + least_packet_awaited_by_peer_ = + *(incoming_ack.received_info.missing_packets.begin()); + } +} + +bool QuicReceivedPacketManager::DontWaitForPacketsBefore( + QuicPacketSequenceNumber least_unacked) { + size_t missing_packets_count = received_info_.missing_packets.size(); + received_info_.missing_packets.erase( + received_info_.missing_packets.begin(), + received_info_.missing_packets.lower_bound(least_unacked)); + return missing_packets_count != received_info_.missing_packets.size(); +} + +void QuicReceivedPacketManager::UpdatePacketInformationSentByPeer( + const QuicAckFrame& incoming_ack) { + // ValidateAck() should fail if peer_least_packet_awaiting_ack_ shrinks. + DCHECK_LE(peer_least_packet_awaiting_ack_, + incoming_ack.sent_info.least_unacked); + if (incoming_ack.sent_info.least_unacked > peer_least_packet_awaiting_ack_) { + bool missed_packets = + DontWaitForPacketsBefore(incoming_ack.sent_info.least_unacked); + if (missed_packets || incoming_ack.sent_info.least_unacked > + received_info_.largest_observed + 1) { + DVLOG(1) << "Updating entropy hashed since we missed packets"; + // There were some missing packets that we won't ever get now. Recalculate + // the received entropy hash. + RecalculateEntropyHash(incoming_ack.sent_info.least_unacked, + incoming_ack.sent_info.entropy_hash); + } + peer_least_packet_awaiting_ack_ = incoming_ack.sent_info.least_unacked; + } + DCHECK(received_info_.missing_packets.empty() || + *received_info_.missing_packets.begin() >= + peer_least_packet_awaiting_ack_); +} + +} // namespace net diff --git a/net/quic/quic_received_packet_manager.h b/net/quic/quic_received_packet_manager.h index e69de29..a762ae8 100644 --- a/net/quic/quic_received_packet_manager.h +++ b/net/quic/quic_received_packet_manager.h @@ -0,0 +1,124 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Manages the packet entropy calculation for both sent and received packets +// for a connection. + +#ifndef NET_QUIC_QUIC_RECEIVED_PACKET_MANAGER_H_ +#define NET_QUIC_QUIC_RECEIVED_PACKET_MANAGER_H_ + +#include "net/quic/quic_framer.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +namespace test { +class QuicReceivedPacketManagerPeer; +} // namespace test + +// Records all received packets by a connection and tracks their entropy. +// Also calculates the correct entropy for the framer when it truncates an ack +// frame being serialized. +class NET_EXPORT_PRIVATE QuicReceivedPacketManager : + public QuicReceivedEntropyHashCalculatorInterface { + public: + QuicReceivedPacketManager(); + virtual ~QuicReceivedPacketManager(); + + // Updates the internal state concerning which packets have been acked. + void RecordPacketReceived(const QuicPacketHeader& header, + QuicTime receipt_time); + + // Checks if we're still waiting for the packet with |sequence_number|. + bool IsAwaitingPacket(QuicPacketSequenceNumber sequence_number); + + // Update the |received_info| for an outgoing ack. + void UpdateReceivedPacketInfo(ReceivedPacketInfo* received_info, + QuicTime approximate_now); + + // QuicReceivedEntropyHashCalculatorInterface + // Called by QuicFramer, when the outgoing ack gets truncated, to recalculate + // the received entropy hash for the truncated ack frame. + virtual QuicPacketEntropyHash EntropyHash( + QuicPacketSequenceNumber sequence_number) const OVERRIDE; + + // These two are called by OnAckFrame. + // + // Updates internal state based on |incoming_ack.received_info|. + void UpdatePacketInformationReceivedByPeer(const QuicAckFrame& incoming_ack); + // Updates internal state based on |incoming_ack.sent_info|. + void UpdatePacketInformationSentByPeer(const QuicAckFrame& incoming_ack); + + QuicPacketSequenceNumber peer_largest_observed_packet() { + return peer_largest_observed_packet_; + } + + QuicPacketSequenceNumber least_packet_awaited_by_peer() { + return least_packet_awaited_by_peer_; + } + + QuicPacketSequenceNumber peer_least_packet_awaiting_ack() { + return peer_least_packet_awaiting_ack_; + } + + private: + friend class test::QuicReceivedPacketManagerPeer; + + typedef std::map<QuicPacketSequenceNumber, + QuicPacketEntropyHash> ReceivedEntropyMap; + + // Record the received entropy hash against |sequence_number|. + void RecordPacketEntropyHash(QuicPacketSequenceNumber sequence_number, + QuicPacketEntropyHash entropy_hash); + + // Recalculate the entropy hash and clears old packet entropies, + // now that the sender sent us the |entropy_hash| for packets up to, + // but not including, |peer_least_unacked|. + void RecalculateEntropyHash(QuicPacketSequenceNumber peer_least_unacked, + QuicPacketEntropyHash entropy_hash); + + // Deletes all missing packets before least unacked. The connection won't + // process any packets with sequence number before |least_unacked| that it + // received after this call. Returns true if there were missing packets before + // |least_unacked| unacked, false otherwise. + bool DontWaitForPacketsBefore(QuicPacketSequenceNumber least_unacked); + + // TODO(satyamshekhar): Can be optimized using an interval set like data + // structure. + // Map of received sequence numbers to their corresponding entropy. + // Every received packet has an entry, and packets without the entropy bit set + // have an entropy value of 0. + // TODO(ianswett): When the entropy flag is off, the entropy should not be 0. + ReceivedEntropyMap packets_entropy_; + + // Cumulative hash of entropy of all received packets. + QuicPacketEntropyHash packets_entropy_hash_; + + // The largest sequence number cleared by RecalculateEntropyHash. + // Received entropy cannot be calculated for numbers less than it. + QuicPacketSequenceNumber largest_sequence_number_; + + + // Track some peer state so we can do less bookkeeping. + // Largest sequence number that the peer has observed. Mostly received, + // missing in case of truncated acks. + QuicPacketSequenceNumber peer_largest_observed_packet_; + // Least sequence number which the peer is still waiting for. + QuicPacketSequenceNumber least_packet_awaited_by_peer_; + // Least sequence number of the the packet sent by the peer for which it + // hasn't received an ack. + QuicPacketSequenceNumber peer_least_packet_awaiting_ack_; + + // Received packet information used to produce acks. + ReceivedPacketInfo received_info_; + + // The time we received the largest_observed sequence number, or zero if + // no sequence numbers have been received since UpdateReceivedPacketInfo. + // Needed for calculating delta_time_largest_observed. + QuicTime time_largest_observed_; +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_RECEIVED_PACKET_MANAGER_H_ diff --git a/net/quic/quic_received_packet_manager_test.cc b/net/quic/quic_received_packet_manager_test.cc index e69de29..76be470 100644 --- a/net/quic/quic_received_packet_manager_test.cc +++ b/net/quic/quic_received_packet_manager_test.cc @@ -0,0 +1,119 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/quic_received_packet_manager.h" + +#include <algorithm> +#include <vector> + +#include "net/quic/test_tools/quic_received_packet_manager_peer.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using std::make_pair; +using std::pair; +using std::vector; + +namespace net { +namespace test { +namespace { + +class QuicReceivedPacketManagerTest : public ::testing::Test { + protected: + void RecordPacketEntropyHash(QuicPacketSequenceNumber sequence_number, + QuicPacketEntropyHash entropy_hash) { + QuicPacketHeader header; + header.packet_sequence_number = sequence_number; + header.entropy_hash = entropy_hash; + received_manager_.RecordPacketReceived(header, QuicTime::Zero());; + } + + QuicReceivedPacketManager received_manager_; +}; + +TEST_F(QuicReceivedPacketManagerTest, ReceivedPacketEntropyHash) { + vector<pair<QuicPacketSequenceNumber, QuicPacketEntropyHash> > entropies; + entropies.push_back(make_pair(1, 12)); + entropies.push_back(make_pair(7, 1)); + entropies.push_back(make_pair(2, 33)); + entropies.push_back(make_pair(5, 3)); + entropies.push_back(make_pair(8, 34)); + + for (size_t i = 0; i < entropies.size(); ++i) { + RecordPacketEntropyHash(entropies[i].first, + entropies[i].second); + } + + sort(entropies.begin(), entropies.end()); + + QuicPacketEntropyHash hash = 0; + size_t index = 0; + for (size_t i = 1; i <= (*entropies.rbegin()).first; ++i) { + if (entropies[index].first == i) { + hash ^= entropies[index].second; + ++index; + } + EXPECT_EQ(hash, received_manager_.EntropyHash(i)); + } +} + +TEST_F(QuicReceivedPacketManagerTest, EntropyHashBelowLeastObserved) { + EXPECT_EQ(0, received_manager_.EntropyHash(0)); + RecordPacketEntropyHash(4, 5); + EXPECT_EQ(0, received_manager_.EntropyHash(3)); +} + +TEST_F(QuicReceivedPacketManagerTest, EntropyHashAboveLargestObserved) { + EXPECT_EQ(0, received_manager_.EntropyHash(0)); + RecordPacketEntropyHash(4, 5); + EXPECT_EQ(0, received_manager_.EntropyHash(3)); +} + +TEST_F(QuicReceivedPacketManagerTest, RecalculateEntropyHash) { + vector<pair<QuicPacketSequenceNumber, QuicPacketEntropyHash> > entropies; + entropies.push_back(make_pair(1, 12)); + entropies.push_back(make_pair(2, 1)); + entropies.push_back(make_pair(3, 33)); + entropies.push_back(make_pair(4, 3)); + entropies.push_back(make_pair(5, 34)); + entropies.push_back(make_pair(6, 29)); + + QuicPacketEntropyHash entropy_hash = 0; + for (size_t i = 0; i < entropies.size(); ++i) { + RecordPacketEntropyHash(entropies[i].first, entropies[i].second); + entropy_hash ^= entropies[i].second; + } + EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(6)); + + // Now set the entropy hash up to 4 to be 100. + entropy_hash ^= 100; + for (size_t i = 0; i < 3; ++i) { + entropy_hash ^= entropies[i].second; + } + QuicReceivedPacketManagerPeer::RecalculateEntropyHash( + &received_manager_, 4, 100); + EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(6)); + + QuicReceivedPacketManagerPeer::RecalculateEntropyHash( + &received_manager_, 1, 50); + EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(6)); +} + +TEST_F(QuicReceivedPacketManagerTest, DontWaitForPacketsBefore) { + QuicPacketHeader header; + header.packet_sequence_number = 2u; + received_manager_.RecordPacketReceived(header, QuicTime::Zero()); + header.packet_sequence_number = 7u; + received_manager_.RecordPacketReceived(header, QuicTime::Zero()); + EXPECT_TRUE(received_manager_.IsAwaitingPacket(3u)); + EXPECT_TRUE(received_manager_.IsAwaitingPacket(6u)); + EXPECT_TRUE(QuicReceivedPacketManagerPeer::DontWaitForPacketsBefore( + &received_manager_, 4)); + EXPECT_FALSE(received_manager_.IsAwaitingPacket(3u)); + EXPECT_TRUE(received_manager_.IsAwaitingPacket(6u)); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/net/quic/quic_session.cc b/net/quic/quic_session.cc index 9785e27..9fb44d4 100644 --- a/net/quic/quic_session.cc +++ b/net/quic/quic_session.cc @@ -12,10 +12,13 @@ using base::StringPiece; using base::hash_map; using base::hash_set; +using std::make_pair; using std::vector; namespace net { +const size_t kMaxPrematurelyClosedStreamsTracked = 20; + #define ENDPOINT (is_server_ ? "Server: " : " Client: ") // We want to make sure we delete any closed streams in a safe manner. @@ -104,9 +107,21 @@ bool QuicSession::OnPacket(const IPEndPoint& self_address, << header.public_header.guid; return false; } + for (size_t i = 0; i < frames.size(); ++i) { // TODO(rch) deal with the error case of stream id 0 - if (IsClosedStream(frames[i].stream_id)) continue; + if (IsClosedStream(frames[i].stream_id)) { + // If we get additional frames for a stream where we didn't process + // headers, it's highly likely our compression context will end up + // permanently out of sync with the peer's, so we give up and close the + // connection. + if (ContainsKey(prematurely_closed_streams_, frames[i].stream_id)) { + connection()->SendConnectionClose( + QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED); + return false; + } + continue; + } ReliableQuicStream* stream = GetStream(frames[i].stream_id); if (stream == NULL) return false; @@ -218,6 +233,13 @@ void QuicSession::CloseStream(QuicStreamId stream_id) { return; } ReliableQuicStream* stream = it->second; + if (!stream->headers_decompressed()) { + if (prematurely_closed_streams_.size() == + kMaxPrematurelyClosedStreamsTracked) { + prematurely_closed_streams_.erase(prematurely_closed_streams_.begin()); + } + prematurely_closed_streams_.insert(make_pair(stream->id(), true)); + } closed_streams_.push_back(it->second); stream_map_.erase(it); stream->OnClose(); diff --git a/net/quic/quic_session.h b/net/quic/quic_session.h index 1c9ab1b..3f0f99f 100644 --- a/net/quic/quic_session.h +++ b/net/quic/quic_session.h @@ -12,6 +12,7 @@ #include "base/compiler_specific.h" #include "base/containers/hash_tables.h" #include "net/base/ip_endpoint.h" +#include "net/base/linked_hash_map.h" #include "net/quic/blocked_list.h" #include "net/quic/quic_connection.h" #include "net/quic/quic_crypto_stream.h" @@ -172,6 +173,8 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { ReliableQuicStream* GetIncomingReliableStream(QuicStreamId stream_id); + ReliableQuicStream* GetStream(const QuicStreamId stream_id); + // This is called after every call other than OnConnectionClose from the // QuicConnectionVisitor to allow post-processing once the work has been done. // In this case, it deletes streams given that it's safe to do so (no other @@ -204,10 +207,13 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { typedef base::hash_map<QuicStreamId, ReliableQuicStream*> ReliableStreamMap; - ReliableQuicStream* GetStream(const QuicStreamId stream_id); - scoped_ptr<QuicConnection> connection_; + // Tracks the last 20 streams which closed without decompressing headers. + // This is for best-effort detection of an unrecoverable compression context. + // Ideally this would be a linked_hash_set as the boolean is unused. + linked_hash_map<QuicStreamId, bool> prematurely_closed_streams_; + // A shim to stand between the connection and the session, to handle stream // deletions. scoped_ptr<VisitorShim> visitor_shim_; diff --git a/net/quic/quic_spdy_decompressor.cc b/net/quic/quic_spdy_decompressor.cc index 23d4633..f8bd4c1 100644 --- a/net/quic/quic_spdy_decompressor.cc +++ b/net/quic/quic_spdy_decompressor.cc @@ -23,21 +23,6 @@ class SpdyFramerVisitor : public SpdyFramerVisitorInterface { virtual void OnError(SpdyFramer* framer) OVERRIDE { error_ = true; } - virtual void OnSynStream(SpdyStreamId stream_id, - SpdyStreamId associated_stream_id, - SpdyPriority priority, - uint8 credential_slot, - bool fin, - bool unidirectional) OVERRIDE {} - virtual void OnSynReply(SpdyStreamId stream_id, bool fin) OVERRIDE {} - virtual void OnHeaders(SpdyStreamId stream_id, bool fin) OVERRIDE {} - virtual bool OnControlFrameHeaderData(SpdyStreamId stream_id, - const char* header_data, - size_t len) OVERRIDE; - virtual bool OnCredentialFrameData(const char* credential_data, - size_t len) OVERRIDE { - return false; - } virtual void OnDataFrameHeader(SpdyStreamId stream_id, size_t length, bool fin) OVERRIDE {} @@ -45,16 +30,31 @@ class SpdyFramerVisitor : public SpdyFramerVisitorInterface { const char* data, size_t len, bool fin) OVERRIDE {} + virtual bool OnControlFrameHeaderData(SpdyStreamId stream_id, + const char* header_data, + size_t len) OVERRIDE; + virtual void OnSynStream(SpdyStreamId stream_id, + SpdyStreamId associated_stream_id, + SpdyPriority priority, + uint8 credential_slot, + bool fin, + bool unidirectional) OVERRIDE {} + virtual void OnSynReply(SpdyStreamId stream_id, bool fin) OVERRIDE {} + virtual void OnRstStream(SpdyStreamId stream_id, + SpdyRstStreamStatus status) OVERRIDE {} virtual void OnSetting(SpdySettingsIds id, uint8 flags, uint32 value) OVERRIDE {} virtual void OnPing(uint32 unique_id) OVERRIDE {} - virtual void OnRstStream(SpdyStreamId stream_id, - SpdyRstStreamStatus status) OVERRIDE {} virtual void OnGoAway(SpdyStreamId last_accepted_stream_id, SpdyGoAwayStatus status) OVERRIDE {} + virtual void OnHeaders(SpdyStreamId stream_id, bool fin) OVERRIDE {} virtual void OnWindowUpdate(SpdyStreamId stream_id, uint32 delta_window_size) OVERRIDE {} + virtual bool OnCredentialFrameData(const char* credential_data, + size_t len) OVERRIDE { + return false; + } virtual void OnPushPromise(SpdyStreamId stream_id, SpdyStreamId promised_stream_id) OVERRIDE {} void set_visitor(QuicSpdyDecompressor::Visitor* visitor) { diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc index b51033f..2f46772 100644 --- a/net/quic/quic_stream_factory_test.cc +++ b/net/quic/quic_stream_factory_test.cc @@ -77,7 +77,7 @@ class QuicStreamFactoryTest : public ::testing::Test { frames.push_back(QuicFrame(&ack)); frames.push_back(QuicFrame(&feedback)); scoped_ptr<QuicPacket> packet( - framer.ConstructFrameDataPacket(header, frames).packet); + framer.BuildUnsizedDataPacket(header, frames).packet); return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket( ENCRYPTION_NONE, header.packet_sequence_number, *packet)); } @@ -110,7 +110,7 @@ class QuicStreamFactoryTest : public ::testing::Test { QuicFrames frames; frames.push_back(frame); scoped_ptr<QuicPacket> packet( - framer.ConstructFrameDataPacket(header, frames).packet); + framer.BuildUnsizedDataPacket(header, frames).packet); return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket( ENCRYPTION_NONE, header.packet_sequence_number, *packet)); } diff --git a/net/quic/reliable_quic_stream.h b/net/quic/reliable_quic_stream.h index 3f5150b..352325d 100644 --- a/net/quic/reliable_quic_stream.h +++ b/net/quic/reliable_quic_stream.h @@ -116,6 +116,8 @@ class NET_EXPORT_PRIVATE ReliableQuicStream : public // Gets the SSL connection information. bool GetSSLInfo(SSLInfo* ssl_info); + bool headers_decompressed() const { return headers_decompressed_; } + protected: // Returns a pair with the number of bytes consumed from data, and a boolean // indicating if the fin bit was consumed. This does not indicate the data diff --git a/net/quic/reliable_quic_stream_test.cc b/net/quic/reliable_quic_stream_test.cc index 5548697..7167a22 100644 --- a/net/quic/reliable_quic_stream_test.cc +++ b/net/quic/reliable_quic_stream_test.cc @@ -124,7 +124,7 @@ TEST_F(ReliableQuicStreamTest, WriteAllData) { connection_->options()->max_packet_length = 1 + QuicPacketCreator::StreamFramePacketOverhead( - PACKET_8BYTE_GUID, !kIncludeVersion, + connection_->version(), PACKET_8BYTE_GUID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); // TODO(rch): figure out how to get StrEq working here. //EXPECT_CALL(*session_, WriteData(kStreamId, StrEq(kData1), _, _)).WillOnce( @@ -192,7 +192,7 @@ TEST_F(ReliableQuicStreamTest, WriteData) { EXPECT_TRUE(write_blocked_list_->IsEmpty()); connection_->options()->max_packet_length = 1 + QuicPacketCreator::StreamFramePacketOverhead( - PACKET_8BYTE_GUID, !kIncludeVersion, + connection_->version(), PACKET_8BYTE_GUID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); // TODO(rch): figure out how to get StrEq working here. //EXPECT_CALL(*session_, WriteData(_, StrEq(kData1), _, _)).WillOnce( diff --git a/net/quic/test_tools/quic_connection_peer.cc b/net/quic/test_tools/quic_connection_peer.cc index 330aa06..bb6b3fc3 100644 --- a/net/quic/test_tools/quic_connection_peer.cc +++ b/net/quic/test_tools/quic_connection_peer.cc @@ -34,10 +34,8 @@ void QuicConnectionPeer::SetSendAlgorithm( } // static -QuicAckFrame* QuicConnectionPeer::GetOutgoingAck( - QuicConnection* connection) { - connection->UpdateOutgoingAck(); - return &connection->outgoing_ack_; +QuicAckFrame* QuicConnectionPeer::CreateAckFrame(QuicConnection* connection) { + return connection->CreateAckFrame(); } // static @@ -106,7 +104,7 @@ bool QuicConnectionPeer::IsValidEntropy( QuicPacketEntropyHash QuicConnectionPeer::ReceivedEntropyHash( QuicConnection* connection, QuicPacketSequenceNumber sequence_number) { - return connection->received_entropy_manager_.EntropyHash( + return connection->received_packet_manager_.EntropyHash( sequence_number); } diff --git a/net/quic/test_tools/quic_connection_peer.h b/net/quic/test_tools/quic_connection_peer.h index 090a1f2..7ce3c5b3 100644 --- a/net/quic/test_tools/quic_connection_peer.h +++ b/net/quic/test_tools/quic_connection_peer.h @@ -36,7 +36,7 @@ class QuicConnectionPeer { static void SetSendAlgorithm(QuicConnection* connection, SendAlgorithmInterface* send_algorithm); - static QuicAckFrame* GetOutgoingAck(QuicConnection* connection); + static QuicAckFrame* CreateAckFrame(QuicConnection* connection); static QuicConnectionVisitorInterface* GetVisitor( QuicConnection* connection); diff --git a/net/quic/test_tools/quic_received_packet_manager_peer.cc b/net/quic/test_tools/quic_received_packet_manager_peer.cc new file mode 100644 index 0000000..d25a209 --- /dev/null +++ b/net/quic/test_tools/quic_received_packet_manager_peer.cc @@ -0,0 +1,30 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/test_tools/quic_received_packet_manager_peer.h" + +#include "net/quic/quic_protocol.h" +#include "net/quic/quic_received_packet_manager.h" + +namespace net { +namespace test { + +// static +void QuicReceivedPacketManagerPeer::RecalculateEntropyHash( + QuicReceivedPacketManager* received_packet_manager, + QuicPacketSequenceNumber peer_least_unacked, + QuicPacketEntropyHash entropy_hash) { + received_packet_manager->RecalculateEntropyHash(peer_least_unacked, + entropy_hash); +} + +// static +bool QuicReceivedPacketManagerPeer::DontWaitForPacketsBefore( + QuicReceivedPacketManager* received_packet_manager, + QuicPacketSequenceNumber least_unacked) { + return received_packet_manager->DontWaitForPacketsBefore(least_unacked); +} + +} // namespace test +} // namespace net diff --git a/net/quic/test_tools/quic_received_packet_manager_peer.h b/net/quic/test_tools/quic_received_packet_manager_peer.h new file mode 100644 index 0000000..4607a0c --- /dev/null +++ b/net/quic/test_tools/quic_received_packet_manager_peer.h @@ -0,0 +1,35 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_TEST_TOOLS_QUIC_RECEIVED_PACKET_MANAGER_PEER_H_ +#define NET_QUIC_TEST_TOOLS_QUIC_RECEIVED_PACKET_MANAGER_PEER_H_ + +#include "net/quic/quic_protocol.h" + +namespace net { + +class QuicReceivedPacketManager; + +namespace test { + +class QuicReceivedPacketManagerPeer { + public: + static void RecalculateEntropyHash( + QuicReceivedPacketManager* received_packet_manager, + QuicPacketSequenceNumber peer_least_unacked, + QuicPacketEntropyHash entropy_hash); + + static bool DontWaitForPacketsBefore( + QuicReceivedPacketManager* received_packet_manager, + QuicPacketSequenceNumber least_unacked); + + private: + DISALLOW_COPY_AND_ASSIGN(QuicReceivedPacketManagerPeer); +}; + +} // namespace test + +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_QUIC_RECEIVED_PACKET_MANAGER_PEER_H_ diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc index 3f122ba..5d1b3a0 100644 --- a/net/quic/test_tools/quic_test_utils.cc +++ b/net/quic/test_tools/quic_test_utils.cc @@ -372,7 +372,7 @@ static QuicPacket* ConstructPacketFromHandshakeMessage( QuicFrame frame(&stream_frame); QuicFrames frames; frames.push_back(frame); - return quic_framer.ConstructFrameDataPacket(header, frames).packet; + return quic_framer.BuildUnsizedDataPacket(header, frames).packet; } QuicPacket* ConstructHandshakePacket(QuicGuid guid, QuicTag tag) { @@ -381,13 +381,15 @@ QuicPacket* ConstructHandshakePacket(QuicGuid guid, QuicTag tag) { return ConstructPacketFromHandshakeMessage(guid, message, false); } -size_t GetPacketLengthForOneStream( - bool include_version, InFecGroup is_in_fec_group, size_t* payload_length) { +size_t GetPacketLengthForOneStream(QuicVersion version, + bool include_version, + InFecGroup is_in_fec_group, + size_t* payload_length) { *payload_length = 1; const size_t stream_length = NullEncrypter().GetCiphertextSize(*payload_length) + QuicPacketCreator::StreamFramePacketOverhead( - PACKET_8BYTE_GUID, include_version, + version, PACKET_8BYTE_GUID, include_version, PACKET_6BYTE_SEQUENCE_NUMBER, is_in_fec_group); const size_t ack_length = NullEncrypter().GetCiphertextSize( QuicFramer::GetMinAckFrameSize()) + @@ -399,10 +401,16 @@ size_t GetPacketLengthForOneStream( return NullEncrypter().GetCiphertextSize(*payload_length) + QuicPacketCreator::StreamFramePacketOverhead( - PACKET_8BYTE_GUID, include_version, + version, PACKET_8BYTE_GUID, include_version, PACKET_6BYTE_SEQUENCE_NUMBER, is_in_fec_group); } +size_t GetMinStreamFrameSize(QuicVersion version) { + return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicMaxStreamOffsetSize + + (version == QUIC_VERSION_6 ? + (kQuicStreamFinSize + kQuicStreamPayloadLengthSize) : 0); +} + QuicPacketEntropyHash TestEntropyCalculator::EntropyHash( QuicPacketSequenceNumber sequence_number) const { return 1u; diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h index a635a4c..f6172c9 100644 --- a/net/quic/test_tools/quic_test_utils.h +++ b/net/quic/test_tools/quic_test_utils.h @@ -38,8 +38,14 @@ void CompareQuicDataWithHexError(const std::string& description, // Returns the length of a QuicPacket that is capable of holding either a // stream frame or a minimal ack frame. Sets |*payload_length| to the number // of bytes of stream data that will fit in such a packet. -size_t GetPacketLengthForOneStream( - bool include_version, InFecGroup is_in_fec_group, size_t* payload_length); +size_t GetPacketLengthForOneStream(QuicVersion version, + bool include_version, + InFecGroup is_in_fec_group, + size_t* payload_length); + +// Size in bytes of the stream frame fields for an arbitrary StreamID and +// offset and the last frame in a packet. +size_t GetMinStreamFrameSize(QuicVersion version); string SerializeUncompressedHeaders(const SpdyHeaderBlock& headers); diff --git a/net/quic/test_tools/reliable_quic_stream_peer.cc b/net/quic/test_tools/reliable_quic_stream_peer.cc index 5119d03..31a64e9 100644 --- a/net/quic/test_tools/reliable_quic_stream_peer.cc +++ b/net/quic/test_tools/reliable_quic_stream_peer.cc @@ -22,5 +22,11 @@ void ReliableQuicStreamPeer::SetStreamBytesWritten( stream->stream_bytes_written_ = stream_bytes_written; } +void ReliableQuicStreamPeer::SetHeadersDecompressed( + ReliableQuicStream* stream, + bool headers_decompressed) { + stream->headers_decompressed_ = headers_decompressed; +} + } // namespace test } // namespace net diff --git a/net/quic/test_tools/reliable_quic_stream_peer.h b/net/quic/test_tools/reliable_quic_stream_peer.h index da229da..346a9b4 100644 --- a/net/quic/test_tools/reliable_quic_stream_peer.h +++ b/net/quic/test_tools/reliable_quic_stream_peer.h @@ -19,6 +19,8 @@ class ReliableQuicStreamPeer { static void SetWriteSideClosed(bool value, ReliableQuicStream* stream); static void SetStreamBytesWritten(QuicStreamOffset stream_bytes_written, ReliableQuicStream* stream); + static void SetHeadersDecompressed(ReliableQuicStream* stream, + bool headers_decompressed); private: DISALLOW_COPY_AND_ASSIGN(ReliableQuicStreamPeer); diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc index 310c393..40449b3 100644 --- a/net/tools/quic/end_to_end_test.cc +++ b/net/tools/quic/end_to_end_test.cc @@ -134,6 +134,7 @@ class EndToEndTest : public ::testing::TestWithParam<QuicVersion> { virtual QuicTestClient* CreateQuicClient() { QuicTestClient* client = new QuicTestClient(server_address_, server_hostname_, + false, // not secure client_config_, version_); client->Connect(); @@ -211,10 +212,10 @@ class EndToEndTest : public ::testing::TestWithParam<QuicVersion> { QuicVersion version_; }; -// Run all end to end tests with QUIC version 6. +// Run all end to end tests with QUIC version 6 and 7. INSTANTIATE_TEST_CASE_P(EndToEndTests, EndToEndTest, - ::testing::Values(QUIC_VERSION_6)); + ::testing::Values(QUIC_VERSION_6, QUIC_VERSION_7)); TEST_P(EndToEndTest, SimpleRequestResponse) { // TODO(rtenneti): Delete this when NSS is supported. @@ -335,9 +336,12 @@ TEST_P(EndToEndTest, RequestOverMultiplePackets) { // are added. // TODO(rch) handle this better when we have different encryption options. - size_t stream_data = 3; - size_t stream_payload_size = QuicFramer::GetMinStreamFrameSize() + - stream_data; + const size_t kStreamDataLength = 3; + const QuicStreamId kStreamId = 1u; + const QuicStreamOffset kStreamOffset = 0u; + size_t stream_payload_size = + QuicFramer::GetMinStreamFrameSize( + GetParam(), kStreamId, kStreamOffset, true) + kStreamDataLength; size_t min_payload_size = std::max(kCongestionFeedbackFrameSize, stream_payload_size); size_t ciphertext_size = NullEncrypter().GetCiphertextSize(min_payload_size); @@ -367,9 +371,12 @@ TEST_P(EndToEndTest, MultipleFramesRandomOrder) { // are added. // TODO(rch) handle this better when we have different encryption options. - size_t stream_data = 3; - size_t stream_payload_size = QuicFramer::GetMinStreamFrameSize() + - stream_data; + const size_t kStreamDataLength = 3; + const QuicStreamId kStreamId = 1u; + const QuicStreamOffset kStreamOffset = 0u; + size_t stream_payload_size = + QuicFramer::GetMinStreamFrameSize( + GetParam(), kStreamId, kStreamOffset, true) + kStreamDataLength; size_t min_payload_size = std::max(kCongestionFeedbackFrameSize, stream_payload_size); size_t ciphertext_size = NullEncrypter().GetCiphertextSize(min_payload_size); diff --git a/net/tools/quic/quic_client.cc b/net/tools/quic/quic_client.cc index 0d3baa9..86fa6c4 100644 --- a/net/tools/quic/quic_client.cc +++ b/net/tools/quic/quic_client.cc @@ -155,8 +155,8 @@ bool QuicClient::StartConnect() { server_hostname_, config_, new QuicConnection(guid, server_address_, - new QuicEpollConnectionHelper(fd_, &epoll_server_), - false, version_), + CreateQuicConnectionHelper(), false, + version_), &crypto_config_)); return session_->CryptoConnect(); } @@ -245,6 +245,10 @@ bool QuicClient::connected() const { session_->connection()->connected(); } +QuicEpollConnectionHelper* QuicClient::CreateQuicConnectionHelper() { + return new QuicEpollConnectionHelper(fd_, &epoll_server_); +} + bool QuicClient::ReadAndProcessPacket() { // Allocate some extra space so we can send an error if the server goes over // the limit. diff --git a/net/tools/quic/quic_client.h b/net/tools/quic/quic_client.h index 2b9b9e7..ca20a8d2 100644 --- a/net/tools/quic/quic_client.h +++ b/net/tools/quic/quic_client.h @@ -28,6 +28,8 @@ class ProofVerifier; namespace tools { +class QuicEpollConnectionHelper; + namespace test { class QuicClientPeer; } // namespace test @@ -139,6 +141,9 @@ class QuicClient : public EpollCallbackInterface { crypto_config_.SetChannelIDSigner(signer); } + protected: + virtual QuicEpollConnectionHelper* CreateQuicConnectionHelper(); + private: friend class net::tools::test::QuicClientPeer; diff --git a/net/tools/quic/quic_client_session_test.cc b/net/tools/quic/quic_client_session_test.cc index bb248c2..1b1ece6 100644 --- a/net/tools/quic/quic_client_session_test.cc +++ b/net/tools/quic/quic_client_session_test.cc @@ -33,7 +33,6 @@ class ToolsQuicClientSessionTest : public ::testing::Test { session_.reset(new QuicClientSession(kServerHostname, QuicConfig(), connection_, &crypto_config_)); session_->config()->SetDefaults(); - session_->config()->set_max_streams_per_connection(1, 1); } void CompleteCryptoHandshake() { @@ -56,7 +55,8 @@ TEST_F(ToolsQuicClientSessionTest, CryptoConnect) { CompleteCryptoHandshake(); } -TEST_F(ToolsQuicClientSessionTest, DISABLED_MaxNumConnections) { +TEST_F(ToolsQuicClientSessionTest, MaxNumStreams) { + session_->config()->set_max_streams_per_connection(1, 1); if (!Aes128Gcm12Encrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; return; diff --git a/net/tools/quic/quic_dispatcher.cc b/net/tools/quic/quic_dispatcher.cc index d702b1d..68691f7 100644 --- a/net/tools/quic/quic_dispatcher.cc +++ b/net/tools/quic/quic_dispatcher.cc @@ -167,7 +167,8 @@ void QuicDispatcher::Shutdown() { void QuicDispatcher::OnConnectionClose(QuicGuid guid, QuicErrorCode error) { SessionMap::iterator it = session_map_.find(guid); if (it == session_map_.end()) { - LOG(DFATAL) << "GUID " << guid << " Does not exist in the session map."; + LOG(DFATAL) << "GUID " << guid << " does not exist in the session map. " + << "Error: " << QuicUtils::ErrorToString(error); return; } diff --git a/net/tools/quic/quic_dispatcher_test.cc b/net/tools/quic/quic_dispatcher_test.cc index 6b7bc15..059d833 100644 --- a/net/tools/quic/quic_dispatcher_test.cc +++ b/net/tools/quic/quic_dispatcher_test.cc @@ -218,7 +218,7 @@ TEST_F(QuicDispatcherTest, TimeWaitListManager) { packet.rejected_sequence_number = 19191; packet.nonce_proof = 132232; scoped_ptr<QuicEncryptedPacket> encrypted( - QuicFramer::ConstructPublicResetPacket(packet)); + QuicFramer::BuildPublicResetPacket(packet)); EXPECT_CALL(*session1_, ConnectionClose(QUIC_PUBLIC_RESET, true)).Times(1) .WillOnce(WithoutArgs(Invoke( reinterpret_cast<MockServerConnection*>(session1_->connection()), diff --git a/net/tools/quic/quic_epoll_connection_helper_test.cc b/net/tools/quic/quic_epoll_connection_helper_test.cc index ac034fd7..0bfaee2 100644 --- a/net/tools/quic/quic_epoll_connection_helper_test.cc +++ b/net/tools/quic/quic_epoll_connection_helper_test.cc @@ -15,6 +15,7 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +using net::test::GetMinStreamFrameSize; using net::test::FramerVisitorCapturingFrames; using net::test::MockSendAlgorithm; using net::test::QuicConnectionPeer; @@ -102,7 +103,7 @@ class QuicEpollConnectionHelperTest : public ::testing::Test { QuicFrames frames; QuicFrame frame(&frame1_); frames.push_back(frame); - return framer_.ConstructFrameDataPacket(header_, frames).packet; + return framer_.BuildUnsizedDataPacket(header_, frames).packet; } QuicGuid guid_; @@ -128,7 +129,7 @@ TEST_F(QuicEpollConnectionHelperTest, DISABLED_TestRetransmission) { const size_t packet_size = GetPacketHeaderSize(PACKET_8BYTE_GUID, kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP) + - QuicFramer::GetMinStreamFrameSize() + arraysize(buffer) - 1; + GetMinStreamFrameSize(framer_.version()) + arraysize(buffer) - 1; EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, packet_size, NOT_RETRANSMISSION)); EXPECT_CALL(*send_algorithm_, AbandoningPacket(1, packet_size)); diff --git a/net/tools/quic/quic_socket_utils.cc b/net/tools/quic/quic_socket_utils.cc index ab66102..4d7d360 100644 --- a/net/tools/quic/quic_socket_utils.cc +++ b/net/tools/quic/quic_socket_utils.cc @@ -144,7 +144,7 @@ int QuicSocketUtils::WritePacket(int fd, const char* buffer, size_t buf_len, hdr.msg_namelen = address_len; hdr.msg_iov = &iov; hdr.msg_iovlen = 1; - hdr.msg_flags = 0; + hdr.msg_flags = 0; const int kSpaceForIpv4 = CMSG_SPACE(sizeof(in_pktinfo)); const int kSpaceForIpv6 = CMSG_SPACE(sizeof(in6_pktinfo)); diff --git a/net/tools/quic/quic_time_wait_list_manager.cc b/net/tools/quic/quic_time_wait_list_manager.cc index 7d5862a..fe538e9 100644 --- a/net/tools/quic/quic_time_wait_list_manager.cc +++ b/net/tools/quic/quic_time_wait_list_manager.cc @@ -93,7 +93,7 @@ class QuicTimeWaitListManager::QueuedPacket { QuicTimeWaitListManager::QuicTimeWaitListManager( QuicPacketWriter* writer, EpollServer* epoll_server) - : framer_(QUIC_VERSION_6, + : framer_(QuicVersionMax(), QuicTime::Zero(), // unused true), epoll_server_(epoll_server), @@ -239,7 +239,7 @@ void QuicTimeWaitListManager::SendPublicReset( QueuedPacket* queued_packet = new QueuedPacket( server_address, client_address, - framer_.ConstructPublicResetPacket(packet)); + framer_.BuildPublicResetPacket(packet)); // Takes ownership of the packet. SendOrQueuePacket(queued_packet); } diff --git a/net/tools/quic/test_tools/quic_test_client.cc b/net/tools/quic/test_tools/quic_test_client.cc index eb26078..03600f2 100644 --- a/net/tools/quic/test_tools/quic_test_client.cc +++ b/net/tools/quic/test_tools/quic_test_client.cc @@ -10,6 +10,7 @@ #include "net/cert/x509_certificate.h" #include "net/quic/crypto/proof_verifier.h" #include "net/tools/flip_server/balsa_headers.h" +#include "net/tools/quic/quic_epoll_connection_helper.h" #include "net/tools/quic/test_tools/http_message_test_utils.h" #include "url/gurl.h" @@ -88,43 +89,81 @@ BalsaHeaders* MungeHeaders(const BalsaHeaders* const_headers, return headers; } +// A quic client which allows mocking out writes. +class QuicEpollClient : public QuicClient { + public: + typedef QuicClient Super; + + QuicEpollClient(IPEndPoint server_address, + const string& server_hostname, + const QuicVersion version) + : Super(server_address, server_hostname, version) { + } + + QuicEpollClient(IPEndPoint server_address, + const string& server_hostname, + const QuicConfig& config, + const QuicVersion version) + : Super(server_address, server_hostname, config, version) { + } + + virtual ~QuicEpollClient() { + if (connected()) { + Disconnect(); + } + } + + virtual QuicEpollConnectionHelper* CreateQuicConnectionHelper() OVERRIDE { + if (writer_.get() != NULL) { + writer_->set_fd(fd()); + return new QuicEpollConnectionHelper(writer_.get(), epoll_server()); + } else { + return Super::CreateQuicConnectionHelper(); + } + } + + void UseWriter(QuicTestWriter* writer) { writer_.reset(writer); } + + private: + scoped_ptr<QuicTestWriter> writer_; +}; + QuicTestClient::QuicTestClient(IPEndPoint address, const string& hostname, const QuicVersion version) - : client_(address, hostname, version) { - Initialize(address, hostname); + : client_(new QuicEpollClient(address, hostname, version)) { + Initialize(address, hostname, true); } QuicTestClient::QuicTestClient(IPEndPoint address, const string& hostname, bool secure, const QuicVersion version) - : client_(address, hostname, version) { - Initialize(address, hostname); - secure_ = secure; - // TODO(alyssar, agl) uncomment here and below when default certs are allowed. - // ExpectCertificates(secure_); + : client_(new QuicEpollClient(address, hostname, version)) { + Initialize(address, hostname, secure); } QuicTestClient::QuicTestClient(IPEndPoint address, const string& hostname, + bool secure, const QuicConfig& config, const QuicVersion version) - : client_(address, hostname, config, version) { - Initialize(address, hostname); + : client_(new QuicEpollClient(address, hostname, config, version)) { + Initialize(address, hostname, secure); } -void QuicTestClient::Initialize(IPEndPoint address, const string& hostname) { +void QuicTestClient::Initialize(IPEndPoint address, + const string& hostname, + bool secure) { server_address_ = address; stream_ = NULL; stream_error_ = QUIC_STREAM_NO_ERROR; - connection_error_ = QUIC_NO_ERROR; bytes_read_ = 0; bytes_written_= 0; never_connected_ = true; - secure_ = true; + secure_ = secure; auto_reconnect_ = false; proof_verifier_ = NULL; - // ExpectCertificates(secure_); + ExpectCertificates(secure_); } QuicTestClient::~QuicTestClient() { @@ -136,10 +175,10 @@ QuicTestClient::~QuicTestClient() { void QuicTestClient::ExpectCertificates(bool on) { if (on) { proof_verifier_ = new RecordingProofVerifier; - client_.SetProofVerifier(proof_verifier_); + client_->SetProofVerifier(proof_verifier_); } else { proof_verifier_ = NULL; - client_.SetProofVerifier(NULL); + client_->SetProofVerifier(NULL); } } @@ -155,7 +194,7 @@ ssize_t QuicTestClient::SendMessage(const HTTPMessage& message) { if (!connected()) { GURL url(message.headers()->request_uri().as_string()); if (!url.host().empty()) { - client_.set_server_hostname(url.host()); + client_->set_server_hostname(url.host()); } } @@ -203,7 +242,7 @@ QuicReliableClientStream* QuicTestClient::GetOrCreateStream() { } } if (!stream_) { - stream_ = client_.CreateReliableClientStream(); + stream_ = client_->CreateReliableClientStream(); if (stream_ != NULL) { stream_->set_visitor(this); } @@ -217,7 +256,7 @@ const string& QuicTestClient::cert_common_name() const { } bool QuicTestClient::connected() const { - return client_.connected(); + return client_->connected(); } void QuicTestClient::WaitForResponse() { @@ -225,13 +264,13 @@ void QuicTestClient::WaitForResponse() { // The client has likely disconnected. return; } - client_.WaitForStreamToClose(stream_->id()); + client_->WaitForStreamToClose(stream_->id()); } void QuicTestClient::Connect() { DCHECK(!connected()); - client_.Initialize(); - client_.Connect(); + client_->Initialize(); + client_->Connect(); never_connected_ = false; } @@ -241,16 +280,15 @@ void QuicTestClient::ResetConnection() { } void QuicTestClient::Disconnect() { - client_.Disconnect(); + client_->Disconnect(); } IPEndPoint QuicTestClient::LocalSocketAddress() const { - return client_.client_address(); + return client_->client_address(); } void QuicTestClient::ClearPerRequestState() { stream_error_ = QUIC_STREAM_NO_ERROR; - connection_error_ = QUIC_NO_ERROR; stream_ = NULL; response_ = ""; headers_.Clear(); @@ -261,7 +299,7 @@ void QuicTestClient::ClearPerRequestState() { void QuicTestClient::WaitForInitialResponse() { DCHECK(stream_ != NULL); while (stream_ && stream_->stream_bytes_read() == 0) { - client_.WaitForEvents(); + client_->WaitForEvents(); } } @@ -288,12 +326,16 @@ void QuicTestClient::OnClose(ReliableQuicStream* stream) { response_ = stream_->data(); headers_.CopyFrom(stream_->headers()); stream_error_ = stream_->stream_error(); - connection_error_ = stream_->connection_error(); bytes_read_ = stream_->stream_bytes_read(); bytes_written_ = stream_->stream_bytes_written(); stream_ = NULL; } +void QuicTestClient::UseWriter(QuicTestWriter* writer) { + DCHECK(!connected()); + reinterpret_cast<QuicEpollClient*>(client_.get())->UseWriter(writer); +} + } // namespace test } // namespace tools } // namespace net diff --git a/net/tools/quic/test_tools/quic_test_client.h b/net/tools/quic/test_tools/quic_test_client.h index 051c011..74bfc24 100644 --- a/net/tools/quic/test_tools/quic_test_client.h +++ b/net/tools/quic/test_tools/quic_test_client.h @@ -8,10 +8,12 @@ #include <string> #include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" #include "net/quic/quic_framer.h" #include "net/quic/quic_packet_creator.h" #include "net/quic/quic_protocol.h" #include "net/tools/quic/quic_client.h" +#include "net/tools/quic/quic_packet_writer.h" namespace net { @@ -21,6 +23,14 @@ namespace tools { namespace test { +// Allows setting a writer for the client's QuicConnectionHelper, to allow +// fine-grained control of writes. +class QuicTestWriter : public QuicPacketWriter { + public: + virtual ~QuicTestWriter() {} + virtual void set_fd(int fd) = 0; +}; + class HTTPMessage; // A toy QUIC client used for testing. @@ -34,6 +44,7 @@ class QuicTestClient : public ReliableQuicStream::Visitor { const QuicVersion version); QuicTestClient(IPEndPoint server_address, const string& server_hostname, + bool secure, const QuicConfig& config, const QuicVersion version); @@ -55,7 +66,7 @@ class QuicTestClient : public ReliableQuicStream::Visitor { // Wraps data in a quic packet and sends it. ssize_t SendData(string data, bool last_data); - QuicPacketCreator::Options* options() { return client_.options(); } + QuicPacketCreator::Options* options() { return client_->options(); } const BalsaHeaders *response_headers() const {return &headers_;} @@ -75,15 +86,17 @@ class QuicTestClient : public ReliableQuicStream::Visitor { // From ReliableQuicStream::Visitor virtual void OnClose(ReliableQuicStream* stream) OVERRIDE; - void SetNextStreamId(QuicStreamId id); + // Configures client_ to take ownership of and use the writer. + // Must be called before initial connect. + void UseWriter(QuicTestWriter* writer); // Returns NULL if the maximum number of streams have already been created. QuicReliableClientStream* GetOrCreateStream(); QuicRstStreamErrorCode stream_error() { return stream_error_; } - QuicErrorCode connection_error() { return connection_error_; } + QuicErrorCode connection_error() { return client()->session()->error(); } - QuicClient* client() { return &client_; } + QuicClient* client() { return client_.get(); } // cert_common_name returns the common name value of the server's certificate, // or the empty string if no certificate was presented. @@ -95,15 +108,14 @@ class QuicTestClient : public ReliableQuicStream::Visitor { void set_auto_reconnect(bool reconnect) { auto_reconnect_ = reconnect; } private: - void Initialize(IPEndPoint address, const string& hostname); + void Initialize(IPEndPoint address, const string& hostname, bool secure); IPEndPoint server_address_; IPEndPoint client_address_; - QuicClient client_; // The actual client + scoped_ptr<QuicClient> client_; // The actual client QuicReliableClientStream* stream_; QuicRstStreamErrorCode stream_error_; - QuicErrorCode connection_error_; BalsaHeaders headers_; string response_; |