1 // Copyright (c) 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "quiche/quic/core/http/quic_spdy_server_stream_base.h"
6
7 #include "absl/strings/str_cat.h"
8 #include "absl/strings/string_view.h"
9 #include "quiche/quic/core/http/quic_spdy_session.h"
10 #include "quiche/quic/core/quic_error_codes.h"
11 #include "quiche/quic/platform/api/quic_flag_utils.h"
12 #include "quiche/quic/platform/api/quic_flags.h"
13 #include "quiche/quic/platform/api/quic_logging.h"
14 #include "quiche/common/quiche_text_utils.h"
15
16 namespace quic {
17
QuicSpdyServerStreamBase(QuicStreamId id,QuicSpdySession * session,StreamType type)18 QuicSpdyServerStreamBase::QuicSpdyServerStreamBase(QuicStreamId id,
19 QuicSpdySession* session,
20 StreamType type)
21 : QuicSpdyStream(id, session, type) {}
22
QuicSpdyServerStreamBase(PendingStream * pending,QuicSpdySession * session)23 QuicSpdyServerStreamBase::QuicSpdyServerStreamBase(PendingStream* pending,
24 QuicSpdySession* session)
25 : QuicSpdyStream(pending, session) {}
26
CloseWriteSide()27 void QuicSpdyServerStreamBase::CloseWriteSide() {
28 if (!fin_received() && !rst_received() && sequencer()->ignore_read_data() &&
29 !rst_sent()) {
30 // Early cancel the stream if it has stopped reading before receiving FIN
31 // or RST.
32 QUICHE_DCHECK(fin_sent() || !session()->connection()->connected());
33 // Tell the peer to stop sending further data.
34 QUIC_DVLOG(1) << " Server: Send QUIC_STREAM_NO_ERROR on stream " << id();
35 MaybeSendStopSending(QUIC_STREAM_NO_ERROR);
36 }
37
38 QuicSpdyStream::CloseWriteSide();
39 }
40
StopReading()41 void QuicSpdyServerStreamBase::StopReading() {
42 if (!fin_received() && !rst_received() && write_side_closed() &&
43 !rst_sent()) {
44 QUICHE_DCHECK(fin_sent());
45 // Tell the peer to stop sending further data.
46 QUIC_DVLOG(1) << " Server: Send QUIC_STREAM_NO_ERROR on stream " << id();
47 MaybeSendStopSending(QUIC_STREAM_NO_ERROR);
48 }
49 QuicSpdyStream::StopReading();
50 }
51
ValidateReceivedHeaders(const QuicHeaderList & header_list)52 bool QuicSpdyServerStreamBase::ValidateReceivedHeaders(
53 const QuicHeaderList& header_list) {
54 if (!QuicSpdyStream::ValidateReceivedHeaders(header_list)) {
55 return false;
56 }
57
58 bool saw_connect = false;
59 bool saw_protocol = false;
60 bool saw_path = false;
61 bool saw_scheme = false;
62 bool saw_method = false;
63 bool saw_authority = false;
64 bool is_extended_connect = false;
65 // Check if it is missing any required headers and if there is any disallowed
66 // ones.
67 for (const std::pair<std::string, std::string>& pair : header_list) {
68 if (pair.first == ":method") {
69 saw_method = true;
70 if (pair.second == "CONNECT") {
71 saw_connect = true;
72 if (saw_protocol) {
73 is_extended_connect = true;
74 }
75 }
76 } else if (pair.first == ":protocol") {
77 saw_protocol = true;
78 if (saw_connect) {
79 is_extended_connect = true;
80 }
81 } else if (pair.first == ":scheme") {
82 saw_scheme = true;
83 } else if (pair.first == ":path") {
84 saw_path = true;
85 } else if (pair.first == ":authority") {
86 saw_authority = true;
87 } else if (absl::StrContains(pair.first, ":")) {
88 set_invalid_request_details(
89 absl::StrCat("Unexpected ':' in header ", pair.first, "."));
90 QUIC_DLOG(ERROR) << invalid_request_details();
91 return false;
92 }
93 if (is_extended_connect) {
94 if (!spdy_session()->allow_extended_connect()) {
95 set_invalid_request_details(
96 "Received extended-CONNECT request while it is disabled.");
97 QUIC_DLOG(ERROR) << invalid_request_details();
98 return false;
99 }
100 } else if (saw_method && !saw_connect) {
101 if (saw_protocol) {
102 set_invalid_request_details(
103 "Received non-CONNECT request with :protocol header.");
104 QUIC_DLOG(ERROR) << "Receive non-CONNECT request with :protocol.";
105 return false;
106 }
107 }
108 }
109
110 if (is_extended_connect) {
111 if (saw_scheme && saw_path && saw_authority) {
112 // Saw all the required pseudo headers.
113 return true;
114 }
115 set_invalid_request_details(
116 "Missing required pseudo headers for extended-CONNECT.");
117 QUIC_DLOG(ERROR) << invalid_request_details();
118 return false;
119 }
120 // This is a vanilla CONNECT or non-CONNECT request.
121 if (saw_connect) {
122 // Check vanilla CONNECT.
123 if (saw_path || saw_scheme) {
124 set_invalid_request_details(
125 "Received invalid CONNECT request with disallowed pseudo header.");
126 QUIC_DLOG(ERROR) << invalid_request_details();
127 return false;
128 }
129 return true;
130 }
131 // Check non-CONNECT request.
132 if (saw_method && saw_authority && saw_path && saw_scheme) {
133 return true;
134 }
135 set_invalid_request_details("Missing required pseudo headers.");
136 QUIC_DLOG(ERROR) << invalid_request_details();
137 return false;
138 }
139
140 } // namespace quic
141