blob: fa9ae5cff48e23d19ce177e19e285d186ee2b749 [file] [log] [blame]
// Copyright 2019 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/core/http/quic_receive_control_stream.h"
#include <utility>
#include "net/third_party/quiche/src/quic/core/http/http_constants.h"
#include "net/third_party/quiche/src/quic/core/http/http_decoder.h"
#include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h"
#include "net/third_party/quiche/src/quic/core/quic_types.h"
#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h"
#include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h"
namespace quic {
// Visitor of HttpDecoder that passes data frame to QuicSpdyStream and closes
// the connection on unexpected frames.
class QuicReceiveControlStream::HttpDecoderVisitor
: public HttpDecoder::Visitor {
public:
explicit HttpDecoderVisitor(QuicReceiveControlStream* stream)
: stream_(stream) {}
HttpDecoderVisitor(const HttpDecoderVisitor&) = delete;
HttpDecoderVisitor& operator=(const HttpDecoderVisitor&) = delete;
void OnError(HttpDecoder* decoder) override {
stream_->OnUnrecoverableError(decoder->error(), decoder->error_detail());
}
bool OnCancelPushFrame(const CancelPushFrame& frame) override {
if (stream_->spdy_session()->debug_visitor()) {
stream_->spdy_session()->debug_visitor()->OnCancelPushFrameReceived(
frame);
}
// TODO(b/151841240): Handle CANCEL_PUSH frames instead of ignoring them.
return true;
}
bool OnMaxPushIdFrame(const MaxPushIdFrame& frame) override {
if (stream_->spdy_session()->perspective() == Perspective::IS_CLIENT) {
OnWrongFrame("Max Push Id");
return false;
}
if (stream_->spdy_session()->debug_visitor()) {
stream_->spdy_session()->debug_visitor()->OnMaxPushIdFrameReceived(frame);
}
// TODO(b/124216424): Signal error if received push ID is smaller than a
// previously received value.
stream_->spdy_session()->OnMaxPushIdFrame(frame.push_id);
return true;
}
bool OnGoAwayFrame(const GoAwayFrame& frame) override {
// TODO(bnc): Check if SETTINGS frame has been received.
if (stream_->spdy_session()->perspective() == Perspective::IS_SERVER) {
OnWrongFrame("Go Away");
return false;
}
if (stream_->spdy_session()->debug_visitor()) {
stream_->spdy_session()->debug_visitor()->OnGoAwayFrameReceived(frame);
}
stream_->spdy_session()->OnHttp3GoAway(frame.stream_id);
return true;
}
bool OnSettingsFrameStart(QuicByteCount header_length) override {
return stream_->OnSettingsFrameStart(header_length);
}
bool OnSettingsFrame(const SettingsFrame& frame) override {
return stream_->OnSettingsFrame(frame);
}
bool OnDataFrameStart(QuicByteCount /*header_length*/, QuicByteCount
/*payload_length*/) override {
OnWrongFrame("Data");
return false;
}
bool OnDataFramePayload(quiche::QuicheStringPiece /*payload*/) override {
OnWrongFrame("Data");
return false;
}
bool OnDataFrameEnd() override {
OnWrongFrame("Data");
return false;
}
bool OnHeadersFrameStart(QuicByteCount /*header_length*/, QuicByteCount
/*payload_length*/) override {
OnWrongFrame("Headers");
return false;
}
bool OnHeadersFramePayload(quiche::QuicheStringPiece /*payload*/) override {
OnWrongFrame("Headers");
return false;
}
bool OnHeadersFrameEnd() override {
OnWrongFrame("Headers");
return false;
}
bool OnPushPromiseFrameStart(QuicByteCount /*header_length*/) override {
OnWrongFrame("Push Promise");
return false;
}
bool OnPushPromiseFramePushId(
PushId /*push_id*/,
QuicByteCount /*push_id_length*/,
QuicByteCount /*header_block_length*/) override {
OnWrongFrame("Push Promise");
return false;
}
bool OnPushPromiseFramePayload(
quiche::QuicheStringPiece /*payload*/) override {
OnWrongFrame("Push Promise");
return false;
}
bool OnPushPromiseFrameEnd() override {
OnWrongFrame("Push Promise");
return false;
}
bool OnPriorityUpdateFrameStart(QuicByteCount header_length) override {
return stream_->OnPriorityUpdateFrameStart(header_length);
}
bool OnPriorityUpdateFrame(const PriorityUpdateFrame& frame) override {
return stream_->OnPriorityUpdateFrame(frame);
}
bool OnUnknownFrameStart(uint64_t frame_type,
QuicByteCount /* header_length */,
QuicByteCount /* payload_length */) override {
if (stream_->spdy_session()->debug_visitor()) {
stream_->spdy_session()->debug_visitor()->OnUnknownFrameStart(
stream_->id(), frame_type);
}
return stream_->OnUnknownFrameStart();
}
bool OnUnknownFramePayload(quiche::QuicheStringPiece payload) override {
if (stream_->spdy_session()->debug_visitor()) {
stream_->spdy_session()->debug_visitor()->OnUnknownFramePayload(
stream_->id(), payload.length());
}
// Ignore unknown frame types.
return true;
}
bool OnUnknownFrameEnd() override {
if (stream_->spdy_session()->debug_visitor()) {
stream_->spdy_session()->debug_visitor()->OnUnknownFrameEnd(
stream_->id());
}
// Ignore unknown frame types.
return true;
}
private:
void OnWrongFrame(quiche::QuicheStringPiece frame_type) {
stream_->OnUnrecoverableError(
QUIC_HTTP_FRAME_UNEXPECTED_ON_CONTROL_STREAM,
quiche::QuicheStrCat(frame_type, " frame received on control stream"));
}
QuicReceiveControlStream* stream_;
};
QuicReceiveControlStream::QuicReceiveControlStream(
PendingStream* pending,
QuicSpdySession* spdy_session)
: QuicStream(pending, READ_UNIDIRECTIONAL, /*is_static=*/true),
settings_frame_received_(false),
http_decoder_visitor_(std::make_unique<HttpDecoderVisitor>(this)),
decoder_(http_decoder_visitor_.get()),
spdy_session_(spdy_session) {
sequencer()->set_level_triggered(true);
}
QuicReceiveControlStream::~QuicReceiveControlStream() {}
void QuicReceiveControlStream::OnStreamReset(
const QuicRstStreamFrame& /*frame*/) {
stream_delegate()->OnStreamError(
QUIC_HTTP_CLOSED_CRITICAL_STREAM,
"RESET_STREAM received for receive control stream");
}
void QuicReceiveControlStream::OnDataAvailable() {
iovec iov;
while (!reading_stopped() && decoder_.error() == QUIC_NO_ERROR &&
sequencer()->GetReadableRegion(&iov)) {
DCHECK(!sequencer()->IsClosed());
QuicByteCount processed_bytes = decoder_.ProcessInput(
reinterpret_cast<const char*>(iov.iov_base), iov.iov_len);
sequencer()->MarkConsumed(processed_bytes);
if (!session()->connection()->connected()) {
return;
}
// The only reason QuicReceiveControlStream pauses HttpDecoder is an error,
// in which case the connection would have already been closed.
DCHECK_EQ(iov.iov_len, processed_bytes);
}
}
bool QuicReceiveControlStream::OnSettingsFrameStart(
QuicByteCount /* header_length */) {
if (settings_frame_received_) {
stream_delegate()->OnStreamError(
QUIC_HTTP_INVALID_FRAME_SEQUENCE_ON_CONTROL_STREAM,
"Settings frames are received twice.");
return false;
}
settings_frame_received_ = true;
return true;
}
bool QuicReceiveControlStream::OnSettingsFrame(const SettingsFrame& settings) {
QUIC_DVLOG(1) << "Control Stream " << id()
<< " received settings frame: " << settings;
if (spdy_session_->debug_visitor() != nullptr) {
spdy_session_->debug_visitor()->OnSettingsFrameReceived(settings);
}
for (const auto& setting : settings.values) {
spdy_session_->OnSetting(setting.first, setting.second);
}
return true;
}
bool QuicReceiveControlStream::OnPriorityUpdateFrameStart(
QuicByteCount /* header_length */) {
if (!settings_frame_received_) {
stream_delegate()->OnStreamError(
QUIC_HTTP_INVALID_FRAME_SEQUENCE_ON_CONTROL_STREAM,
"PRIORITY_UPDATE frame received before SETTINGS.");
return false;
}
return true;
}
bool QuicReceiveControlStream::OnPriorityUpdateFrame(
const PriorityUpdateFrame& priority) {
if (spdy_session()->debug_visitor()) {
spdy_session()->debug_visitor()->OnPriorityUpdateFrameReceived(priority);
}
// TODO(b/147306124): Use a proper structured headers parser instead.
for (auto key_value :
quiche::QuicheTextUtils::Split(priority.priority_field_value, ',')) {
auto key_and_value = quiche::QuicheTextUtils::Split(key_value, '=');
if (key_and_value.size() != 2) {
continue;
}
quiche::QuicheStringPiece key = key_and_value[0];
quiche::QuicheTextUtils::RemoveLeadingAndTrailingWhitespace(&key);
if (key != "u") {
continue;
}
quiche::QuicheStringPiece value = key_and_value[1];
int urgency;
if (!quiche::QuicheTextUtils::StringToInt(value, &urgency) || urgency < 0 ||
urgency > 7) {
stream_delegate()->OnStreamError(
QUIC_INVALID_STREAM_ID,
"Invalid value for PRIORITY_UPDATE urgency parameter.");
return false;
}
if (priority.prioritized_element_type == REQUEST_STREAM) {
return spdy_session_->OnPriorityUpdateForRequestStream(
priority.prioritized_element_id, urgency);
} else {
return spdy_session_->OnPriorityUpdateForPushStream(
priority.prioritized_element_id, urgency);
}
}
// Ignore frame if no urgency parameter can be parsed.
return true;
}
bool QuicReceiveControlStream::OnUnknownFrameStart() {
if (!settings_frame_received_) {
stream_delegate()->OnStreamError(
QUIC_HTTP_INVALID_FRAME_SEQUENCE_ON_CONTROL_STREAM,
"Unknown frame received before SETTINGS.");
return false;
}
return true;
}
} // namespace quic