| // 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 "quic/core/http/quic_spdy_stream_body_manager.h" |
| |
| #include <algorithm> |
| |
| #include "absl/strings/string_view.h" |
| #include "quic/platform/api/quic_logging.h" |
| |
| namespace quic { |
| |
| QuicSpdyStreamBodyManager::QuicSpdyStreamBodyManager() |
| : total_body_bytes_received_(0) {} |
| |
| size_t QuicSpdyStreamBodyManager::OnNonBody(QuicByteCount length) { |
| QUICHE_DCHECK_NE(0u, length); |
| |
| if (fragments_.empty()) { |
| // Non-body bytes can be consumed immediately, because all previously |
| // received body bytes have been read. |
| return length; |
| } |
| |
| // Non-body bytes will be consumed after last body fragment is read. |
| fragments_.back().trailing_non_body_byte_count += length; |
| return 0; |
| } |
| |
| void QuicSpdyStreamBodyManager::OnBody(absl::string_view body) { |
| QUICHE_DCHECK(!body.empty()); |
| |
| fragments_.push_back({body, 0}); |
| total_body_bytes_received_ += body.length(); |
| } |
| |
| size_t QuicSpdyStreamBodyManager::OnBodyConsumed(size_t num_bytes) { |
| QuicByteCount bytes_to_consume = 0; |
| size_t remaining_bytes = num_bytes; |
| |
| while (remaining_bytes > 0) { |
| if (fragments_.empty()) { |
| QUIC_BUG(quic_bug_10394_1) << "Not enough available body to consume."; |
| return 0; |
| } |
| |
| Fragment& fragment = fragments_.front(); |
| const absl::string_view body = fragment.body; |
| |
| if (body.length() > remaining_bytes) { |
| // Consume leading |remaining_bytes| bytes of body. |
| bytes_to_consume += remaining_bytes; |
| fragment.body = body.substr(remaining_bytes); |
| return bytes_to_consume; |
| } |
| |
| // Consume entire fragment and the following |
| // |trailing_non_body_byte_count| bytes. |
| remaining_bytes -= body.length(); |
| bytes_to_consume += body.length() + fragment.trailing_non_body_byte_count; |
| fragments_.pop_front(); |
| } |
| |
| return bytes_to_consume; |
| } |
| |
| int QuicSpdyStreamBodyManager::PeekBody(iovec* iov, size_t iov_len) const { |
| QUICHE_DCHECK(iov); |
| QUICHE_DCHECK_GT(iov_len, 0u); |
| |
| // TODO(bnc): Is this really necessary? |
| if (fragments_.empty()) { |
| iov[0].iov_base = nullptr; |
| iov[0].iov_len = 0; |
| return 0; |
| } |
| |
| size_t iov_filled = 0; |
| while (iov_filled < fragments_.size() && iov_filled < iov_len) { |
| absl::string_view body = fragments_[iov_filled].body; |
| iov[iov_filled].iov_base = const_cast<char*>(body.data()); |
| iov[iov_filled].iov_len = body.size(); |
| iov_filled++; |
| } |
| |
| return iov_filled; |
| } |
| |
| size_t QuicSpdyStreamBodyManager::ReadBody(const struct iovec* iov, |
| size_t iov_len, |
| size_t* total_bytes_read) { |
| *total_bytes_read = 0; |
| QuicByteCount bytes_to_consume = 0; |
| |
| // The index of iovec to write to. |
| size_t index = 0; |
| // Address to write to within current iovec. |
| char* dest = reinterpret_cast<char*>(iov[index].iov_base); |
| // Remaining space in current iovec. |
| size_t dest_remaining = iov[index].iov_len; |
| |
| while (!fragments_.empty()) { |
| Fragment& fragment = fragments_.front(); |
| const absl::string_view body = fragment.body; |
| |
| const size_t bytes_to_copy = |
| std::min<size_t>(body.length(), dest_remaining); |
| memcpy(dest, body.data(), bytes_to_copy); |
| bytes_to_consume += bytes_to_copy; |
| *total_bytes_read += bytes_to_copy; |
| |
| if (bytes_to_copy == body.length()) { |
| // Entire fragment read. |
| bytes_to_consume += fragment.trailing_non_body_byte_count; |
| fragments_.pop_front(); |
| } else { |
| // Consume leading |bytes_to_copy| bytes of body. |
| fragment.body = body.substr(bytes_to_copy); |
| } |
| |
| if (bytes_to_copy == dest_remaining) { |
| // Current iovec full. |
| ++index; |
| if (index == iov_len) { |
| break; |
| } |
| dest = reinterpret_cast<char*>(iov[index].iov_base); |
| dest_remaining = iov[index].iov_len; |
| } else { |
| // Advance destination parameters within this iovec. |
| dest += bytes_to_copy; |
| dest_remaining -= bytes_to_copy; |
| } |
| } |
| |
| return bytes_to_consume; |
| } |
| |
| } // namespace quic |