| // Copyright (c) 2018 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/third_party/quiche/src/quic/test_tools/simple_session_notifier.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_utils.h" | |
| #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" | |
| #include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h" | |
| #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" | |
| namespace quic { | |
| namespace test { | |
| SimpleSessionNotifier::SimpleSessionNotifier(QuicConnection* connection) | |
| : last_control_frame_id_(kInvalidControlFrameId), | |
| least_unacked_(1), | |
| least_unsent_(1), | |
| connection_(connection) {} | |
| SimpleSessionNotifier::~SimpleSessionNotifier() { | |
| while (!control_frames_.empty()) { | |
| DeleteFrame(&control_frames_.front()); | |
| control_frames_.pop_front(); | |
| } | |
| } | |
| SimpleSessionNotifier::StreamState::StreamState() | |
| : bytes_total(0), | |
| bytes_sent(0), | |
| fin_buffered(false), | |
| fin_sent(false), | |
| fin_outstanding(false), | |
| fin_lost(false) {} | |
| SimpleSessionNotifier::StreamState::~StreamState() {} | |
| QuicConsumedData SimpleSessionNotifier::WriteOrBufferData( | |
| QuicStreamId id, | |
| QuicByteCount data_length, | |
| StreamSendingState state) { | |
| if (!QuicContainsKey(stream_map_, id)) { | |
| stream_map_[id] = StreamState(); | |
| } | |
| StreamState& stream_state = stream_map_.find(id)->second; | |
| const bool had_buffered_data = | |
| HasBufferedStreamData() || HasBufferedControlFrames(); | |
| QuicConsumedData total_consumed(0, false); | |
| QuicStreamOffset offset = stream_state.bytes_sent; | |
| QUIC_DVLOG(1) << "WriteOrBuffer stream_id: " << id << " [" << offset << ", " | |
| << offset + data_length << "), fin: " << (state != NO_FIN); | |
| stream_state.bytes_total += data_length; | |
| stream_state.fin_buffered = state != NO_FIN; | |
| if (had_buffered_data) { | |
| QUIC_DLOG(WARNING) << "Connection is write blocked"; | |
| return {0, false}; | |
| } | |
| const size_t length = stream_state.bytes_total - stream_state.bytes_sent; | |
| connection_->SetTransmissionType(NOT_RETRANSMISSION); | |
| QuicConsumedData consumed = | |
| connection_->SendStreamData(id, length, stream_state.bytes_sent, | |
| stream_state.fin_buffered ? FIN : NO_FIN); | |
| QUIC_DVLOG(1) << "consumed: " << consumed; | |
| OnStreamDataConsumed(id, stream_state.bytes_sent, consumed.bytes_consumed, | |
| consumed.fin_consumed); | |
| return consumed; | |
| } | |
| void SimpleSessionNotifier::OnStreamDataConsumed(QuicStreamId id, | |
| QuicStreamOffset offset, | |
| QuicByteCount data_length, | |
| bool fin) { | |
| StreamState& state = stream_map_.find(id)->second; | |
| if (QuicUtils::IsCryptoStreamId(connection_->transport_version(), id) && | |
| data_length > 0) { | |
| crypto_bytes_transferred_[connection_->encryption_level()].Add( | |
| offset, offset + data_length); | |
| } | |
| state.bytes_sent += data_length; | |
| state.fin_sent = fin; | |
| state.fin_outstanding = fin; | |
| } | |
| size_t SimpleSessionNotifier::WriteCryptoData(EncryptionLevel level, | |
| QuicByteCount data_length, | |
| QuicStreamOffset offset) { | |
| crypto_state_[level].bytes_total += data_length; | |
| size_t bytes_written = | |
| connection_->SendCryptoData(level, data_length, offset); | |
| crypto_state_[level].bytes_sent += bytes_written; | |
| crypto_bytes_transferred_[level].Add(offset, offset + bytes_written); | |
| return bytes_written; | |
| } | |
| void SimpleSessionNotifier::WriteOrBufferRstStream( | |
| QuicStreamId id, | |
| QuicRstStreamErrorCode error, | |
| QuicStreamOffset bytes_written) { | |
| QUIC_DVLOG(1) << "Writing RST_STREAM_FRAME"; | |
| const bool had_buffered_data = | |
| HasBufferedStreamData() || HasBufferedControlFrames(); | |
| control_frames_.emplace_back((QuicFrame(new QuicRstStreamFrame( | |
| ++last_control_frame_id_, id, error, bytes_written)))); | |
| if (error != QUIC_STREAM_NO_ERROR) { | |
| // Delete stream to avoid retransmissions. | |
| stream_map_.erase(id); | |
| } | |
| if (had_buffered_data) { | |
| QUIC_DLOG(WARNING) << "Connection is write blocked"; | |
| return; | |
| } | |
| WriteBufferedControlFrames(); | |
| } | |
| void SimpleSessionNotifier::WriteOrBufferPing() { | |
| QUIC_DVLOG(1) << "Writing PING_FRAME"; | |
| const bool had_buffered_data = | |
| HasBufferedStreamData() || HasBufferedControlFrames(); | |
| control_frames_.emplace_back( | |
| (QuicFrame(QuicPingFrame(++last_control_frame_id_)))); | |
| if (had_buffered_data) { | |
| QUIC_DLOG(WARNING) << "Connection is write blocked"; | |
| return; | |
| } | |
| WriteBufferedControlFrames(); | |
| } | |
| void SimpleSessionNotifier::NeuterUnencryptedData() { | |
| // TODO(nharper): Handle CRYPTO frame case. | |
| if (QuicVersionUsesCryptoFrames(connection_->transport_version())) { | |
| return; | |
| } | |
| for (const auto& interval : crypto_bytes_transferred_[ENCRYPTION_INITIAL]) { | |
| QuicStreamFrame stream_frame( | |
| QuicUtils::GetCryptoStreamId(connection_->transport_version()), false, | |
| interval.min(), interval.max() - interval.min()); | |
| OnFrameAcked(QuicFrame(stream_frame), QuicTime::Delta::Zero(), | |
| QuicTime::Zero()); | |
| } | |
| } | |
| void SimpleSessionNotifier::OnCanWrite() { | |
| if (!RetransmitLostCryptoData() || !RetransmitLostControlFrames() || | |
| !RetransmitLostStreamData()) { | |
| return; | |
| } | |
| // Write buffered control frames. | |
| if (!WriteBufferedControlFrames()) { | |
| return; | |
| } | |
| // Write new data. | |
| for (const auto& pair : stream_map_) { | |
| const auto& state = pair.second; | |
| if (!StreamHasBufferedData(pair.first)) { | |
| continue; | |
| } | |
| const size_t length = state.bytes_total - state.bytes_sent; | |
| const bool can_bundle_fin = | |
| state.fin_buffered && (state.bytes_sent + length == state.bytes_total); | |
| connection_->SetTransmissionType(NOT_RETRANSMISSION); | |
| QuicConsumedData consumed = connection_->SendStreamData( | |
| pair.first, length, state.bytes_sent, can_bundle_fin ? FIN : NO_FIN); | |
| QUIC_DVLOG(1) << "Tries to write stream_id: " << pair.first << " [" | |
| << state.bytes_sent << ", " << state.bytes_sent + length | |
| << "), fin: " << can_bundle_fin | |
| << ", and consumed: " << consumed; | |
| OnStreamDataConsumed(pair.first, state.bytes_sent, consumed.bytes_consumed, | |
| consumed.fin_consumed); | |
| if (length != consumed.bytes_consumed || | |
| (can_bundle_fin && !consumed.fin_consumed)) { | |
| break; | |
| } | |
| } | |
| } | |
| void SimpleSessionNotifier::OnStreamReset(QuicStreamId id, | |
| QuicRstStreamErrorCode error) { | |
| if (error != QUIC_STREAM_NO_ERROR) { | |
| // Delete stream to avoid retransmissions. | |
| stream_map_.erase(id); | |
| } | |
| } | |
| bool SimpleSessionNotifier::WillingToWrite() const { | |
| QUIC_DVLOG(1) << "has_buffered_control_frames: " << HasBufferedControlFrames() | |
| << " as_lost_control_frames: " << !lost_control_frames_.empty() | |
| << " has_buffered_stream_data: " << HasBufferedStreamData() | |
| << " has_lost_stream_data: " << HasLostStreamData(); | |
| return HasBufferedControlFrames() || !lost_control_frames_.empty() || | |
| HasBufferedStreamData() || HasLostStreamData(); | |
| } | |
| QuicByteCount SimpleSessionNotifier::StreamBytesSent() const { | |
| QuicByteCount bytes_sent = 0; | |
| for (const auto& pair : stream_map_) { | |
| const auto& state = pair.second; | |
| bytes_sent += state.bytes_sent; | |
| } | |
| return bytes_sent; | |
| } | |
| QuicByteCount SimpleSessionNotifier::StreamBytesToSend() const { | |
| QuicByteCount bytes_to_send = 0; | |
| for (const auto& pair : stream_map_) { | |
| const auto& state = pair.second; | |
| bytes_to_send += (state.bytes_total - state.bytes_sent); | |
| } | |
| return bytes_to_send; | |
| } | |
| bool SimpleSessionNotifier::OnFrameAcked(const QuicFrame& frame, | |
| QuicTime::Delta /*ack_delay_time*/, | |
| QuicTime /*receive_timestamp*/) { | |
| QUIC_DVLOG(1) << "Acking " << frame; | |
| if (frame.type == CRYPTO_FRAME) { | |
| StreamState* state = &crypto_state_[frame.crypto_frame->level]; | |
| QuicStreamOffset offset = frame.crypto_frame->offset; | |
| QuicByteCount data_length = frame.crypto_frame->data_length; | |
| QuicIntervalSet<QuicStreamOffset> newly_acked(offset, offset + data_length); | |
| newly_acked.Difference(state->bytes_acked); | |
| if (newly_acked.Empty()) { | |
| return false; | |
| } | |
| state->bytes_acked.Add(offset, offset + data_length); | |
| state->pending_retransmissions.Difference(offset, offset + data_length); | |
| return true; | |
| } | |
| if (frame.type != STREAM_FRAME) { | |
| return OnControlFrameAcked(frame); | |
| } | |
| if (!QuicContainsKey(stream_map_, frame.stream_frame.stream_id)) { | |
| return false; | |
| } | |
| auto* state = &stream_map_.find(frame.stream_frame.stream_id)->second; | |
| QuicStreamOffset offset = frame.stream_frame.offset; | |
| QuicByteCount data_length = frame.stream_frame.data_length; | |
| QuicIntervalSet<QuicStreamOffset> newly_acked(offset, offset + data_length); | |
| newly_acked.Difference(state->bytes_acked); | |
| const bool fin_newly_acked = frame.stream_frame.fin && state->fin_outstanding; | |
| if (newly_acked.Empty() && !fin_newly_acked) { | |
| return false; | |
| } | |
| state->bytes_acked.Add(offset, offset + data_length); | |
| if (fin_newly_acked) { | |
| state->fin_outstanding = false; | |
| state->fin_lost = false; | |
| } | |
| state->pending_retransmissions.Difference(offset, offset + data_length); | |
| return true; | |
| } | |
| void SimpleSessionNotifier::OnFrameLost(const QuicFrame& frame) { | |
| QUIC_DVLOG(1) << "Losting " << frame; | |
| if (frame.type == CRYPTO_FRAME) { | |
| StreamState* state = &crypto_state_[frame.crypto_frame->level]; | |
| QuicStreamOffset offset = frame.crypto_frame->offset; | |
| QuicByteCount data_length = frame.crypto_frame->data_length; | |
| QuicIntervalSet<QuicStreamOffset> bytes_lost(offset, offset + data_length); | |
| bytes_lost.Difference(state->bytes_acked); | |
| if (bytes_lost.Empty()) { | |
| return; | |
| } | |
| for (const auto& lost : bytes_lost) { | |
| state->pending_retransmissions.Add(lost.min(), lost.max()); | |
| } | |
| return; | |
| } | |
| if (frame.type != STREAM_FRAME) { | |
| OnControlFrameLost(frame); | |
| return; | |
| } | |
| if (!QuicContainsKey(stream_map_, frame.stream_frame.stream_id)) { | |
| return; | |
| } | |
| auto* state = &stream_map_.find(frame.stream_frame.stream_id)->second; | |
| QuicStreamOffset offset = frame.stream_frame.offset; | |
| QuicByteCount data_length = frame.stream_frame.data_length; | |
| QuicIntervalSet<QuicStreamOffset> bytes_lost(offset, offset + data_length); | |
| bytes_lost.Difference(state->bytes_acked); | |
| const bool fin_lost = state->fin_outstanding && frame.stream_frame.fin; | |
| if (bytes_lost.Empty() && !fin_lost) { | |
| return; | |
| } | |
| for (const auto& lost : bytes_lost) { | |
| state->pending_retransmissions.Add(lost.min(), lost.max()); | |
| } | |
| state->fin_lost = fin_lost; | |
| } | |
| void SimpleSessionNotifier::RetransmitFrames(const QuicFrames& frames, | |
| TransmissionType type) { | |
| QuicConnection::ScopedPacketFlusher retransmission_flusher(connection_); | |
| connection_->SetTransmissionType(type); | |
| for (const QuicFrame& frame : frames) { | |
| if (frame.type == CRYPTO_FRAME) { | |
| const StreamState& state = crypto_state_[frame.crypto_frame->level]; | |
| QuicIntervalSet<QuicStreamOffset> retransmission( | |
| frame.crypto_frame->offset, | |
| frame.crypto_frame->offset + frame.crypto_frame->data_length); | |
| retransmission.Difference(state.bytes_acked); | |
| for (const auto& interval : retransmission) { | |
| QuicStreamOffset offset = interval.min(); | |
| QuicByteCount length = interval.max() - interval.min(); | |
| size_t consumed = connection_->SendCryptoData(frame.crypto_frame->level, | |
| length, offset); | |
| // CRYPTO frames should never be write blocked. | |
| DCHECK_EQ(consumed, length); | |
| } | |
| } | |
| if (frame.type != STREAM_FRAME) { | |
| if (GetControlFrameId(frame) == kInvalidControlFrameId) { | |
| continue; | |
| } | |
| QuicFrame copy = CopyRetransmittableControlFrame(frame); | |
| if (!connection_->SendControlFrame(copy)) { | |
| // Connection is write blocked. | |
| DeleteFrame(©); | |
| return; | |
| } | |
| continue; | |
| } | |
| if (!QuicContainsKey(stream_map_, frame.stream_frame.stream_id)) { | |
| continue; | |
| } | |
| const auto& state = stream_map_.find(frame.stream_frame.stream_id)->second; | |
| QuicIntervalSet<QuicStreamOffset> retransmission( | |
| frame.stream_frame.offset, | |
| frame.stream_frame.offset + frame.stream_frame.data_length); | |
| EncryptionLevel retransmission_encryption_level = | |
| connection_->encryption_level(); | |
| EncryptionLevel current_encryption_level = connection_->encryption_level(); | |
| if (QuicUtils::IsCryptoStreamId(connection_->transport_version(), | |
| frame.stream_frame.stream_id)) { | |
| for (size_t i = 0; i < NUM_ENCRYPTION_LEVELS; ++i) { | |
| if (retransmission.Intersects(crypto_bytes_transferred_[i])) { | |
| retransmission_encryption_level = static_cast<EncryptionLevel>(i); | |
| retransmission.Intersection(crypto_bytes_transferred_[i]); | |
| break; | |
| } | |
| } | |
| } | |
| retransmission.Difference(state.bytes_acked); | |
| bool retransmit_fin = frame.stream_frame.fin && state.fin_outstanding; | |
| QuicConsumedData consumed(0, false); | |
| for (const auto& interval : retransmission) { | |
| QuicStreamOffset retransmission_offset = interval.min(); | |
| QuicByteCount retransmission_length = interval.max() - interval.min(); | |
| const bool can_bundle_fin = | |
| retransmit_fin && | |
| (retransmission_offset + retransmission_length == state.bytes_sent); | |
| if (QuicUtils::IsCryptoStreamId(connection_->transport_version(), | |
| frame.stream_frame.stream_id)) { | |
| // Set appropriate encryption level for crypto stream. | |
| connection_->SetDefaultEncryptionLevel(retransmission_encryption_level); | |
| } | |
| consumed = connection_->SendStreamData( | |
| frame.stream_frame.stream_id, retransmission_length, | |
| retransmission_offset, can_bundle_fin ? FIN : NO_FIN); | |
| QUIC_DVLOG(1) << "stream " << frame.stream_frame.stream_id | |
| << " is forced to retransmit stream data [" | |
| << retransmission_offset << ", " | |
| << retransmission_offset + retransmission_length | |
| << ") and fin: " << can_bundle_fin | |
| << ", consumed: " << consumed; | |
| if (can_bundle_fin) { | |
| retransmit_fin = !consumed.fin_consumed; | |
| } | |
| if (QuicUtils::IsCryptoStreamId(connection_->transport_version(), | |
| frame.stream_frame.stream_id)) { | |
| // Restore encryption level. | |
| connection_->SetDefaultEncryptionLevel(current_encryption_level); | |
| } | |
| if (consumed.bytes_consumed < retransmission_length || | |
| (can_bundle_fin && !consumed.fin_consumed)) { | |
| // Connection is write blocked. | |
| return; | |
| } | |
| } | |
| if (retransmit_fin) { | |
| QUIC_DVLOG(1) << "stream " << frame.stream_frame.stream_id | |
| << " retransmits fin only frame."; | |
| consumed = connection_->SendStreamData(frame.stream_frame.stream_id, 0, | |
| state.bytes_sent, FIN); | |
| } | |
| } | |
| } | |
| bool SimpleSessionNotifier::IsFrameOutstanding(const QuicFrame& frame) const { | |
| if (frame.type == CRYPTO_FRAME) { | |
| QuicStreamOffset offset = frame.crypto_frame->offset; | |
| QuicByteCount data_length = frame.crypto_frame->data_length; | |
| bool ret = data_length > 0 && | |
| !crypto_state_[frame.crypto_frame->level].bytes_acked.Contains( | |
| offset, offset + data_length); | |
| return ret; | |
| } | |
| if (frame.type != STREAM_FRAME) { | |
| return IsControlFrameOutstanding(frame); | |
| } | |
| if (!QuicContainsKey(stream_map_, frame.stream_frame.stream_id)) { | |
| return false; | |
| } | |
| const auto& state = stream_map_.find(frame.stream_frame.stream_id)->second; | |
| QuicStreamOffset offset = frame.stream_frame.offset; | |
| QuicByteCount data_length = frame.stream_frame.data_length; | |
| return (data_length > 0 && | |
| !state.bytes_acked.Contains(offset, offset + data_length)) || | |
| (frame.stream_frame.fin && state.fin_outstanding); | |
| } | |
| bool SimpleSessionNotifier::HasUnackedCryptoData() const { | |
| if (QuicVersionUsesCryptoFrames(connection_->transport_version())) { | |
| for (size_t i = 0; i < NUM_ENCRYPTION_LEVELS; ++i) { | |
| const StreamState& state = crypto_state_[i]; | |
| if (state.bytes_total > state.bytes_sent) { | |
| return true; | |
| } | |
| QuicIntervalSet<QuicStreamOffset> bytes_to_ack(0, state.bytes_total); | |
| bytes_to_ack.Difference(state.bytes_acked); | |
| if (!bytes_to_ack.Empty()) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| if (!QuicContainsKey(stream_map_, QuicUtils::GetCryptoStreamId( | |
| connection_->transport_version()))) { | |
| return false; | |
| } | |
| const auto& state = | |
| stream_map_ | |
| .find(QuicUtils::GetCryptoStreamId(connection_->transport_version())) | |
| ->second; | |
| if (state.bytes_total > state.bytes_sent) { | |
| return true; | |
| } | |
| QuicIntervalSet<QuicStreamOffset> bytes_to_ack(0, state.bytes_total); | |
| bytes_to_ack.Difference(state.bytes_acked); | |
| return !bytes_to_ack.Empty(); | |
| } | |
| bool SimpleSessionNotifier::HasUnackedStreamData() const { | |
| for (auto it : stream_map_) { | |
| if (StreamIsWaitingForAcks(it.first)) | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool SimpleSessionNotifier::OnControlFrameAcked(const QuicFrame& frame) { | |
| QuicControlFrameId id = GetControlFrameId(frame); | |
| if (id == kInvalidControlFrameId) { | |
| return false; | |
| } | |
| DCHECK(id < least_unacked_ + control_frames_.size()); | |
| if (id < least_unacked_ || | |
| GetControlFrameId(control_frames_.at(id - least_unacked_)) == | |
| kInvalidControlFrameId) { | |
| return false; | |
| } | |
| SetControlFrameId(kInvalidControlFrameId, | |
| &control_frames_.at(id - least_unacked_)); | |
| lost_control_frames_.erase(id); | |
| while (!control_frames_.empty() && | |
| GetControlFrameId(control_frames_.front()) == kInvalidControlFrameId) { | |
| DeleteFrame(&control_frames_.front()); | |
| control_frames_.pop_front(); | |
| ++least_unacked_; | |
| } | |
| return true; | |
| } | |
| void SimpleSessionNotifier::OnControlFrameLost(const QuicFrame& frame) { | |
| QuicControlFrameId id = GetControlFrameId(frame); | |
| if (id == kInvalidControlFrameId) { | |
| return; | |
| } | |
| DCHECK(id < least_unacked_ + control_frames_.size()); | |
| if (id < least_unacked_ || | |
| GetControlFrameId(control_frames_.at(id - least_unacked_)) == | |
| kInvalidControlFrameId) { | |
| return; | |
| } | |
| if (!QuicContainsKey(lost_control_frames_, id)) { | |
| lost_control_frames_[id] = true; | |
| } | |
| } | |
| bool SimpleSessionNotifier::IsControlFrameOutstanding( | |
| const QuicFrame& frame) const { | |
| QuicControlFrameId id = GetControlFrameId(frame); | |
| if (id == kInvalidControlFrameId) { | |
| return false; | |
| } | |
| return id < least_unacked_ + control_frames_.size() && id >= least_unacked_ && | |
| GetControlFrameId(control_frames_.at(id - least_unacked_)) != | |
| kInvalidControlFrameId; | |
| } | |
| bool SimpleSessionNotifier::RetransmitLostControlFrames() { | |
| while (!lost_control_frames_.empty()) { | |
| QuicFrame pending = control_frames_.at(lost_control_frames_.begin()->first - | |
| least_unacked_); | |
| QuicFrame copy = CopyRetransmittableControlFrame(pending); | |
| connection_->SetTransmissionType(LOSS_RETRANSMISSION); | |
| if (!connection_->SendControlFrame(copy)) { | |
| // Connection is write blocked. | |
| DeleteFrame(©); | |
| break; | |
| } | |
| lost_control_frames_.pop_front(); | |
| } | |
| return lost_control_frames_.empty(); | |
| } | |
| bool SimpleSessionNotifier::RetransmitLostCryptoData() { | |
| if (QuicVersionUsesCryptoFrames(connection_->transport_version())) { | |
| for (EncryptionLevel level : | |
| {ENCRYPTION_INITIAL, ENCRYPTION_HANDSHAKE, ENCRYPTION_ZERO_RTT, | |
| ENCRYPTION_FORWARD_SECURE}) { | |
| auto& state = crypto_state_[level]; | |
| while (!state.pending_retransmissions.Empty()) { | |
| connection_->SetTransmissionType(HANDSHAKE_RETRANSMISSION); | |
| EncryptionLevel current_encryption_level = | |
| connection_->encryption_level(); | |
| connection_->SetDefaultEncryptionLevel(level); | |
| QuicIntervalSet<QuicStreamOffset> retransmission( | |
| state.pending_retransmissions.begin()->min(), | |
| state.pending_retransmissions.begin()->max()); | |
| retransmission.Intersection(crypto_bytes_transferred_[level]); | |
| QuicStreamOffset retransmission_offset = retransmission.begin()->min(); | |
| QuicByteCount retransmission_length = | |
| retransmission.begin()->max() - retransmission.begin()->min(); | |
| size_t bytes_consumed = connection_->SendCryptoData( | |
| level, retransmission_length, retransmission_offset); | |
| // Restore encryption level. | |
| connection_->SetDefaultEncryptionLevel(current_encryption_level); | |
| state.pending_retransmissions.Difference( | |
| retransmission_offset, retransmission_offset + bytes_consumed); | |
| if (bytes_consumed < retransmission_length) { | |
| return false; | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| if (!QuicContainsKey(stream_map_, QuicUtils::GetCryptoStreamId( | |
| connection_->transport_version()))) { | |
| return true; | |
| } | |
| auto& state = | |
| stream_map_ | |
| .find(QuicUtils::GetCryptoStreamId(connection_->transport_version())) | |
| ->second; | |
| while (!state.pending_retransmissions.Empty()) { | |
| connection_->SetTransmissionType(HANDSHAKE_RETRANSMISSION); | |
| QuicIntervalSet<QuicStreamOffset> retransmission( | |
| state.pending_retransmissions.begin()->min(), | |
| state.pending_retransmissions.begin()->max()); | |
| EncryptionLevel retransmission_encryption_level = ENCRYPTION_INITIAL; | |
| for (size_t i = 0; i < NUM_ENCRYPTION_LEVELS; ++i) { | |
| if (retransmission.Intersects(crypto_bytes_transferred_[i])) { | |
| retransmission_encryption_level = static_cast<EncryptionLevel>(i); | |
| retransmission.Intersection(crypto_bytes_transferred_[i]); | |
| break; | |
| } | |
| } | |
| QuicStreamOffset retransmission_offset = retransmission.begin()->min(); | |
| QuicByteCount retransmission_length = | |
| retransmission.begin()->max() - retransmission.begin()->min(); | |
| EncryptionLevel current_encryption_level = connection_->encryption_level(); | |
| // Set appropriate encryption level. | |
| connection_->SetDefaultEncryptionLevel(retransmission_encryption_level); | |
| QuicConsumedData consumed = connection_->SendStreamData( | |
| QuicUtils::GetCryptoStreamId(connection_->transport_version()), | |
| retransmission_length, retransmission_offset, NO_FIN); | |
| // Restore encryption level. | |
| connection_->SetDefaultEncryptionLevel(current_encryption_level); | |
| state.pending_retransmissions.Difference( | |
| retransmission_offset, retransmission_offset + consumed.bytes_consumed); | |
| if (consumed.bytes_consumed < retransmission_length) { | |
| break; | |
| } | |
| } | |
| return state.pending_retransmissions.Empty(); | |
| } | |
| bool SimpleSessionNotifier::RetransmitLostStreamData() { | |
| for (auto& pair : stream_map_) { | |
| StreamState& state = pair.second; | |
| QuicConsumedData consumed(0, false); | |
| while (!state.pending_retransmissions.Empty() || state.fin_lost) { | |
| connection_->SetTransmissionType(LOSS_RETRANSMISSION); | |
| if (state.pending_retransmissions.Empty()) { | |
| QUIC_DVLOG(1) << "stream " << pair.first | |
| << " retransmits fin only frame."; | |
| consumed = | |
| connection_->SendStreamData(pair.first, 0, state.bytes_sent, FIN); | |
| state.fin_lost = !consumed.fin_consumed; | |
| if (state.fin_lost) { | |
| QUIC_DLOG(INFO) << "Connection is write blocked"; | |
| return false; | |
| } | |
| } else { | |
| QuicStreamOffset offset = state.pending_retransmissions.begin()->min(); | |
| QuicByteCount length = state.pending_retransmissions.begin()->max() - | |
| state.pending_retransmissions.begin()->min(); | |
| const bool can_bundle_fin = | |
| state.fin_lost && (offset + length == state.bytes_sent); | |
| consumed = connection_->SendStreamData(pair.first, length, offset, | |
| can_bundle_fin ? FIN : NO_FIN); | |
| QUIC_DVLOG(1) << "stream " << pair.first | |
| << " tries to retransmit stream data [" << offset << ", " | |
| << offset + length << ") and fin: " << can_bundle_fin | |
| << ", consumed: " << consumed; | |
| state.pending_retransmissions.Difference( | |
| offset, offset + consumed.bytes_consumed); | |
| if (consumed.fin_consumed) { | |
| state.fin_lost = false; | |
| } | |
| if (length > consumed.bytes_consumed || | |
| (can_bundle_fin && !consumed.fin_consumed)) { | |
| QUIC_DVLOG(1) << "Connection is write blocked"; | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| return !HasLostStreamData(); | |
| } | |
| bool SimpleSessionNotifier::WriteBufferedControlFrames() { | |
| while (HasBufferedControlFrames()) { | |
| QuicFrame frame_to_send = | |
| control_frames_.at(least_unsent_ - least_unacked_); | |
| QuicFrame copy = CopyRetransmittableControlFrame(frame_to_send); | |
| connection_->SetTransmissionType(NOT_RETRANSMISSION); | |
| if (!connection_->SendControlFrame(copy)) { | |
| // Connection is write blocked. | |
| DeleteFrame(©); | |
| break; | |
| } | |
| ++least_unsent_; | |
| } | |
| return !HasBufferedControlFrames(); | |
| } | |
| bool SimpleSessionNotifier::HasBufferedControlFrames() const { | |
| return least_unsent_ < least_unacked_ + control_frames_.size(); | |
| } | |
| bool SimpleSessionNotifier::HasBufferedStreamData() const { | |
| for (const auto& pair : stream_map_) { | |
| const auto& state = pair.second; | |
| if (state.bytes_total > state.bytes_sent || | |
| (state.fin_buffered && !state.fin_sent)) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| bool SimpleSessionNotifier::StreamIsWaitingForAcks(QuicStreamId id) const { | |
| if (!QuicContainsKey(stream_map_, id)) { | |
| return false; | |
| } | |
| const StreamState& state = stream_map_.find(id)->second; | |
| return !state.bytes_acked.Contains(0, state.bytes_sent) || | |
| state.fin_outstanding; | |
| } | |
| bool SimpleSessionNotifier::StreamHasBufferedData(QuicStreamId id) const { | |
| if (!QuicContainsKey(stream_map_, id)) { | |
| return false; | |
| } | |
| const StreamState& state = stream_map_.find(id)->second; | |
| return state.bytes_total > state.bytes_sent || | |
| (state.fin_buffered && !state.fin_sent); | |
| } | |
| bool SimpleSessionNotifier::HasLostStreamData() const { | |
| for (const auto& pair : stream_map_) { | |
| const auto& state = pair.second; | |
| if (!state.pending_retransmissions.Empty() || state.fin_lost) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| } // namespace test | |
| } // namespace quic |