| // Copyright (c) 2016 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_server_stream_base.h" |
| |
| #include "absl/strings/string_view.h" |
| #include "quic/core/http/quic_spdy_session.h" |
| #include "quic/core/quic_error_codes.h" |
| #include "quic/platform/api/quic_flag_utils.h" |
| #include "quic/platform/api/quic_flags.h" |
| #include "quic/platform/api/quic_logging.h" |
| #include "common/quiche_text_utils.h" |
| |
| namespace quic { |
| |
| QuicSpdyServerStreamBase::QuicSpdyServerStreamBase(QuicStreamId id, |
| QuicSpdySession* session, |
| StreamType type) |
| : QuicSpdyStream(id, session, type) {} |
| |
| QuicSpdyServerStreamBase::QuicSpdyServerStreamBase(PendingStream* pending, |
| QuicSpdySession* session) |
| : QuicSpdyStream(pending, session) {} |
| |
| void QuicSpdyServerStreamBase::CloseWriteSide() { |
| if (!fin_received() && !rst_received() && sequencer()->ignore_read_data() && |
| !rst_sent()) { |
| // Early cancel the stream if it has stopped reading before receiving FIN |
| // or RST. |
| QUICHE_DCHECK(fin_sent() || !session()->connection()->connected()); |
| // Tell the peer to stop sending further data. |
| QUIC_DVLOG(1) << " Server: Send QUIC_STREAM_NO_ERROR on stream " << id(); |
| MaybeSendStopSending(QUIC_STREAM_NO_ERROR); |
| } |
| |
| QuicSpdyStream::CloseWriteSide(); |
| } |
| |
| void QuicSpdyServerStreamBase::StopReading() { |
| if (!fin_received() && !rst_received() && write_side_closed() && |
| !rst_sent()) { |
| QUICHE_DCHECK(fin_sent()); |
| // Tell the peer to stop sending further data. |
| QUIC_DVLOG(1) << " Server: Send QUIC_STREAM_NO_ERROR on stream " << id(); |
| MaybeSendStopSending(QUIC_STREAM_NO_ERROR); |
| } |
| QuicSpdyStream::StopReading(); |
| } |
| |
| bool QuicSpdyServerStreamBase::AreHeadersValid( |
| const QuicHeaderList& header_list) const { |
| if (!GetQuicReloadableFlag(quic_verify_request_headers_2)) { |
| return true; |
| } |
| QUIC_RELOADABLE_FLAG_COUNT_N(quic_verify_request_headers_2, 2, 3); |
| if (!QuicSpdyStream::AreHeadersValid(header_list)) { |
| return false; |
| } |
| |
| bool saw_connect = false; |
| bool saw_protocol = false; |
| bool saw_path = false; |
| bool saw_scheme = false; |
| bool saw_method = false; |
| bool saw_authority = false; |
| bool is_extended_connect = false; |
| // Check if it is missing any required headers and if there is any disallowed |
| // ones. |
| for (const std::pair<std::string, std::string>& pair : header_list) { |
| if (pair.first == ":method") { |
| saw_method = true; |
| if (pair.second == "CONNECT") { |
| saw_connect = true; |
| if (saw_protocol) { |
| is_extended_connect = true; |
| } |
| } |
| } else if (pair.first == ":protocol") { |
| saw_protocol = true; |
| if (saw_connect) { |
| is_extended_connect = true; |
| } |
| } else if (pair.first == ":scheme") { |
| saw_scheme = true; |
| } else if (pair.first == ":path") { |
| saw_path = true; |
| } else if (pair.first == ":authority") { |
| saw_authority = true; |
| } else if (absl::StrContains(pair.first, ":")) { |
| QUIC_DLOG(ERROR) << "Unexpected ':' in header " << pair.first << "."; |
| return false; |
| } |
| if (is_extended_connect) { |
| if (!spdy_session()->allow_extended_connect()) { |
| QUIC_DLOG(ERROR) |
| << "Received extended-CONNECT request while it is disabled."; |
| return false; |
| } |
| } else if (saw_method && !saw_connect) { |
| if (saw_protocol) { |
| QUIC_DLOG(ERROR) << "Receive non-CONNECT request with :protocol."; |
| return false; |
| } |
| } |
| } |
| |
| if (is_extended_connect) { |
| if (saw_scheme && saw_path && saw_authority) { |
| // Saw all the required pseudo headers. |
| return true; |
| } |
| QUIC_DLOG(ERROR) << "Missing required pseudo headers for extended-CONNECT."; |
| return false; |
| } |
| // This is a vanilla CONNECT or non-CONNECT request. |
| if (saw_connect) { |
| // Check vanilla CONNECT. |
| if (saw_path || saw_scheme) { |
| QUIC_DLOG(ERROR) |
| << "Received invalid CONNECT request with disallowed pseudo header."; |
| return false; |
| } |
| return true; |
| } |
| // Check non-CONNECT request. |
| if (saw_method && saw_authority && saw_path && saw_scheme) { |
| return true; |
| } |
| QUIC_LOG(ERROR) << "Missing required pseudo headers."; |
| return false; |
| } |
| |
| } // namespace quic |