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