blob: 2ae24d472b759a81e75b349e7a2401d3bba39477 [file] [log] [blame]
// 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