1 // Copyright (c) 2012 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 "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h"
6
7 #include <utility>
8
9 #include "absl/strings/string_view.h"
10 #include "net/third_party/quiche/src/quic/core/http/quic_client_promised_info.h"
11 #include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h"
12 #include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
13 #include "net/third_party/quiche/src/quic/core/quic_alarm.h"
14 #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
15 #include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
16
17 using spdy::SpdyHeaderBlock;
18
19 namespace quic {
20
QuicSpdyClientStream(QuicStreamId id,QuicSpdyClientSession * session,StreamType type)21 QuicSpdyClientStream::QuicSpdyClientStream(QuicStreamId id,
22 QuicSpdyClientSession* session,
23 StreamType type)
24 : QuicSpdyStream(id, session, type),
25 content_length_(-1),
26 response_code_(0),
27 header_bytes_read_(0),
28 header_bytes_written_(0),
29 session_(session),
30 has_preliminary_headers_(false) {}
31
QuicSpdyClientStream(PendingStream * pending,QuicSpdyClientSession * session,StreamType type)32 QuicSpdyClientStream::QuicSpdyClientStream(PendingStream* pending,
33 QuicSpdyClientSession* session,
34 StreamType type)
35 : QuicSpdyStream(pending, session, type),
36 content_length_(-1),
37 response_code_(0),
38 header_bytes_read_(0),
39 header_bytes_written_(0),
40 session_(session),
41 has_preliminary_headers_(false) {}
42
43 QuicSpdyClientStream::~QuicSpdyClientStream() = default;
44
OnInitialHeadersComplete(bool fin,size_t frame_len,const QuicHeaderList & header_list)45 void QuicSpdyClientStream::OnInitialHeadersComplete(
46 bool fin,
47 size_t frame_len,
48 const QuicHeaderList& header_list) {
49 QuicSpdyStream::OnInitialHeadersComplete(fin, frame_len, header_list);
50
51 DCHECK(headers_decompressed());
52 header_bytes_read_ += frame_len;
53 if (!SpdyUtils::CopyAndValidateHeaders(header_list, &content_length_,
54 &response_headers_)) {
55 QUIC_DLOG(ERROR) << "Failed to parse header list: "
56 << header_list.DebugString() << " on stream " << id();
57 Reset(QUIC_BAD_APPLICATION_PAYLOAD);
58 return;
59 }
60
61 if (!ParseHeaderStatusCode(response_headers_, &response_code_)) {
62 QUIC_DLOG(ERROR) << "Received invalid response code: "
63 << response_headers_[":status"].as_string()
64 << " on stream " << id();
65 Reset(QUIC_BAD_APPLICATION_PAYLOAD);
66 return;
67 }
68
69 if (response_code_ == 101) {
70 // 101 "Switching Protocols" is forbidden in HTTP/3 as per the
71 // "HTTP Upgrade" section of draft-ietf-quic-http.
72 QUIC_DLOG(ERROR) << "Received forbidden 101 response code"
73 << " on stream " << id();
74 Reset(QUIC_BAD_APPLICATION_PAYLOAD);
75 return;
76 }
77
78 if (response_code_ >= 100 && response_code_ < 200) {
79 // These are Informational 1xx headers, not the actual response headers.
80 QUIC_DLOG(INFO) << "Received informational response code: "
81 << response_headers_[":status"].as_string() << " on stream "
82 << id();
83 set_headers_decompressed(false);
84 if (response_code_ == 100 && !has_preliminary_headers_) {
85 // This is 100 Continue, save it to enable "Expect: 100-continue".
86 has_preliminary_headers_ = true;
87 preliminary_headers_ = std::move(response_headers_);
88 } else {
89 response_headers_.clear();
90 }
91 }
92
93 ConsumeHeaderList();
94 QUIC_DVLOG(1) << "headers complete for stream " << id();
95
96 session_->OnInitialHeadersComplete(id(), response_headers_);
97 }
98
OnTrailingHeadersComplete(bool fin,size_t frame_len,const QuicHeaderList & header_list)99 void QuicSpdyClientStream::OnTrailingHeadersComplete(
100 bool fin,
101 size_t frame_len,
102 const QuicHeaderList& header_list) {
103 QuicSpdyStream::OnTrailingHeadersComplete(fin, frame_len, header_list);
104 MarkTrailersConsumed();
105 }
106
OnPromiseHeaderList(QuicStreamId promised_id,size_t frame_len,const QuicHeaderList & header_list)107 void QuicSpdyClientStream::OnPromiseHeaderList(
108 QuicStreamId promised_id,
109 size_t frame_len,
110 const QuicHeaderList& header_list) {
111 header_bytes_read_ += frame_len;
112 int64_t content_length = -1;
113 SpdyHeaderBlock promise_headers;
114 if (!SpdyUtils::CopyAndValidateHeaders(header_list, &content_length,
115 &promise_headers)) {
116 QUIC_DLOG(ERROR) << "Failed to parse promise headers: "
117 << header_list.DebugString();
118 Reset(QUIC_BAD_APPLICATION_PAYLOAD);
119 return;
120 }
121
122 session_->HandlePromised(id(), promised_id, promise_headers);
123 if (visitor() != nullptr) {
124 visitor()->OnPromiseHeadersComplete(promised_id, frame_len);
125 }
126 }
127
OnBodyAvailable()128 void QuicSpdyClientStream::OnBodyAvailable() {
129 // For push streams, visitor will not be set until the rendezvous
130 // between server promise and client request is complete.
131 if (visitor() == nullptr)
132 return;
133
134 while (HasBytesToRead()) {
135 struct iovec iov;
136 if (GetReadableRegions(&iov, 1) == 0) {
137 // No more data to read.
138 break;
139 }
140 QUIC_DVLOG(1) << "Client processed " << iov.iov_len << " bytes for stream "
141 << id();
142 data_.append(static_cast<char*>(iov.iov_base), iov.iov_len);
143
144 if (content_length_ >= 0 &&
145 data_.size() > static_cast<uint64_t>(content_length_)) {
146 QUIC_DLOG(ERROR) << "Invalid content length (" << content_length_
147 << ") with data of size " << data_.size();
148 Reset(QUIC_BAD_APPLICATION_PAYLOAD);
149 return;
150 }
151 MarkConsumed(iov.iov_len);
152 }
153 if (sequencer()->IsClosed()) {
154 OnFinRead();
155 } else {
156 sequencer()->SetUnblocked();
157 }
158 }
159
SendRequest(SpdyHeaderBlock headers,absl::string_view body,bool fin)160 size_t QuicSpdyClientStream::SendRequest(SpdyHeaderBlock headers,
161 absl::string_view body,
162 bool fin) {
163 QuicConnection::ScopedPacketFlusher flusher(session_->connection());
164 bool send_fin_with_headers = fin && body.empty();
165 size_t bytes_sent = body.size();
166 header_bytes_written_ =
167 WriteHeaders(std::move(headers), send_fin_with_headers, nullptr);
168 bytes_sent += header_bytes_written_;
169
170 if (!body.empty()) {
171 WriteOrBufferBody(body, fin);
172 }
173
174 return bytes_sent;
175 }
176
177 } // namespace quic
178