blob: 05a731001cf196e005d495954cacc5dc07ae5563 [file] [log] [blame]
QUICHE teama6ef0a62019-03-07 20:34:33 -05001// 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/tools/quic_simple_server_stream.h"
6
7#include <list>
8#include <utility>
9
10#include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h"
11#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
12#include "net/third_party/quiche/src/quic/core/quic_utils.h"
13#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
14#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
15#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
16#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
QUICHE teama6ef0a62019-03-07 20:34:33 -050017#include "net/third_party/quiche/src/quic/tools/quic_simple_server_session.h"
QUICHE team5015e2e2019-12-11 09:38:06 -080018#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h"
19#include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h"
QUICHE teama6ef0a62019-03-07 20:34:33 -050020#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
21
22using spdy::SpdyHeaderBlock;
23
24namespace quic {
25
26QuicSimpleServerStream::QuicSimpleServerStream(
27 QuicStreamId id,
28 QuicSpdySession* session,
29 StreamType type,
30 QuicSimpleServerBackend* quic_simple_server_backend)
31 : QuicSpdyServerStreamBase(id, session, type),
32 content_length_(-1),
rch7bd54762019-10-15 10:53:24 -070033 generate_bytes_length_(0),
rchd7d9d782019-05-02 18:18:03 -070034 quic_simple_server_backend_(quic_simple_server_backend) {
35 DCHECK(quic_simple_server_backend_);
36}
QUICHE teama6ef0a62019-03-07 20:34:33 -050037
38QuicSimpleServerStream::QuicSimpleServerStream(
renjietangbaea59c2019-05-29 15:08:14 -070039 PendingStream* pending,
QUICHE teama6ef0a62019-03-07 20:34:33 -050040 QuicSpdySession* session,
41 StreamType type,
42 QuicSimpleServerBackend* quic_simple_server_backend)
renjietangbaea59c2019-05-29 15:08:14 -070043 : QuicSpdyServerStreamBase(pending, session, type),
QUICHE teama6ef0a62019-03-07 20:34:33 -050044 content_length_(-1),
rch7bd54762019-10-15 10:53:24 -070045 generate_bytes_length_(0),
rchd7d9d782019-05-02 18:18:03 -070046 quic_simple_server_backend_(quic_simple_server_backend) {
47 DCHECK(quic_simple_server_backend_);
48}
QUICHE teama6ef0a62019-03-07 20:34:33 -050049
50QuicSimpleServerStream::~QuicSimpleServerStream() {
rchd7d9d782019-05-02 18:18:03 -070051 quic_simple_server_backend_->CloseBackendResponseStream(this);
QUICHE teama6ef0a62019-03-07 20:34:33 -050052}
53
54void QuicSimpleServerStream::OnInitialHeadersComplete(
55 bool fin,
56 size_t frame_len,
57 const QuicHeaderList& header_list) {
58 QuicSpdyStream::OnInitialHeadersComplete(fin, frame_len, header_list);
59 if (!SpdyUtils::CopyAndValidateHeaders(header_list, &content_length_,
60 &request_headers_)) {
61 QUIC_DVLOG(1) << "Invalid headers";
62 SendErrorResponse();
63 }
64 ConsumeHeaderList();
65}
66
67void QuicSimpleServerStream::OnTrailingHeadersComplete(
dschinazi17d42422019-06-18 16:35:07 -070068 bool /*fin*/,
69 size_t /*frame_len*/,
70 const QuicHeaderList& /*header_list*/) {
QUICHE teama6ef0a62019-03-07 20:34:33 -050071 QUIC_BUG << "Server does not support receiving Trailers.";
72 SendErrorResponse();
73}
74
75void QuicSimpleServerStream::OnBodyAvailable() {
76 while (HasBytesToRead()) {
77 struct iovec iov;
78 if (GetReadableRegions(&iov, 1) == 0) {
79 // No more data to read.
80 break;
81 }
82 QUIC_DVLOG(1) << "Stream " << id() << " processed " << iov.iov_len
83 << " bytes.";
84 body_.append(static_cast<char*>(iov.iov_base), iov.iov_len);
85
86 if (content_length_ >= 0 &&
87 body_.size() > static_cast<uint64_t>(content_length_)) {
88 QUIC_DVLOG(1) << "Body size (" << body_.size() << ") > content length ("
89 << content_length_ << ").";
90 SendErrorResponse();
91 return;
92 }
93 MarkConsumed(iov.iov_len);
94 }
95 if (!sequencer()->IsClosed()) {
96 sequencer()->SetUnblocked();
97 return;
98 }
99
100 // If the sequencer is closed, then all the body, including the fin, has been
101 // consumed.
102 OnFinRead();
103
104 if (write_side_closed() || fin_buffered()) {
105 return;
106 }
107
108 SendResponse();
109}
110
111void QuicSimpleServerStream::PushResponse(
112 SpdyHeaderBlock push_request_headers) {
renjietangd1d00852019-09-06 10:43:12 -0700113 if (QuicUtils::IsClientInitiatedStreamId(session()->transport_version(),
114 id())) {
QUICHE teama6ef0a62019-03-07 20:34:33 -0500115 QUIC_BUG << "Client initiated stream shouldn't be used as promised stream.";
116 return;
117 }
118 // Change the stream state to emulate a client request.
119 request_headers_ = std::move(push_request_headers);
120 content_length_ = 0;
121 QUIC_DVLOG(1) << "Stream " << id()
122 << " ready to receive server push response.";
123 DCHECK(reading_stopped());
124
125 // Directly send response based on the emulated request_headers_.
126 SendResponse();
127}
128
129void QuicSimpleServerStream::SendResponse() {
130 if (request_headers_.empty()) {
131 QUIC_DVLOG(1) << "Request headers empty.";
132 SendErrorResponse();
133 return;
134 }
135
136 if (content_length_ > 0 &&
137 static_cast<uint64_t>(content_length_) != body_.size()) {
138 QUIC_DVLOG(1) << "Content length (" << content_length_ << ") != body size ("
139 << body_.size() << ").";
140 SendErrorResponse();
141 return;
142 }
143
144 if (!QuicContainsKey(request_headers_, ":authority") ||
145 !QuicContainsKey(request_headers_, ":path")) {
146 QUIC_DVLOG(1) << "Request headers do not contain :authority or :path.";
147 SendErrorResponse();
148 return;
149 }
150
dschinazid70d98e2019-05-01 17:30:16 -0700151 if (quic_simple_server_backend_ == nullptr) {
152 QUIC_DVLOG(1) << "Backend is missing.";
153 SendErrorResponse();
154 return;
155 }
156
QUICHE teama6ef0a62019-03-07 20:34:33 -0500157 // Fetch the response from the backend interface and wait for callback once
158 // response is ready
159 quic_simple_server_backend_->FetchResponseFromBackend(request_headers_, body_,
160 this);
161}
162
163QuicConnectionId QuicSimpleServerStream::connection_id() const {
164 return spdy_session()->connection_id();
165}
166
167QuicStreamId QuicSimpleServerStream::stream_id() const {
168 return id();
169}
170
vasilvvc48c8712019-03-11 13:38:16 -0700171std::string QuicSimpleServerStream::peer_host() const {
QUICHE teama6ef0a62019-03-07 20:34:33 -0500172 return spdy_session()->peer_address().host().ToString();
173}
174
175void QuicSimpleServerStream::OnResponseBackendComplete(
176 const QuicBackendResponse* response,
177 std::list<QuicBackendResponse::ServerPushInfo> resources) {
178 if (response == nullptr) {
179 QUIC_DVLOG(1) << "Response not found in cache.";
180 SendNotFoundResponse();
181 return;
182 }
183
184 if (response->response_type() == QuicBackendResponse::CLOSE_CONNECTION) {
185 QUIC_DVLOG(1) << "Special response: closing connection.";
186 CloseConnectionWithDetails(QUIC_NO_ERROR, "Toy server forcing close");
187 return;
188 }
189
190 if (response->response_type() == QuicBackendResponse::IGNORE_REQUEST) {
191 QUIC_DVLOG(1) << "Special response: ignoring request.";
192 return;
193 }
194
195 if (response->response_type() == QuicBackendResponse::BACKEND_ERR_RESPONSE) {
196 QUIC_DVLOG(1) << "Quic Proxy: Backend connection error.";
197 /*502 Bad Gateway
198 The server was acting as a gateway or proxy and received an
199 invalid response from the upstream server.*/
200 SendErrorResponse(502);
201 return;
202 }
203
204 // Examing response status, if it was not pure integer as typical h2
205 // response status, send error response. Notice that
206 // QuicHttpResponseCache push urls are strictly authority + path only,
207 // scheme is not included (see |QuicHttpResponseCache::GetKey()|).
vasilvvc48c8712019-03-11 13:38:16 -0700208 std::string request_url = request_headers_[":authority"].as_string() +
209 request_headers_[":path"].as_string();
QUICHE teama6ef0a62019-03-07 20:34:33 -0500210 int response_code;
211 const SpdyHeaderBlock& response_headers = response->headers();
212 if (!ParseHeaderStatusCode(response_headers, &response_code)) {
213 auto status = response_headers.find(":status");
214 if (status == response_headers.end()) {
215 QUIC_LOG(WARNING)
216 << ":status not present in response from cache for request "
217 << request_url;
218 } else {
219 QUIC_LOG(WARNING) << "Illegal (non-integer) response :status from cache: "
220 << status->second << " for request " << request_url;
221 }
222 SendErrorResponse();
223 return;
224 }
225
renjietangd1d00852019-09-06 10:43:12 -0700226 if (QuicUtils::IsServerInitiatedStreamId(session()->transport_version(),
227 id())) {
QUICHE teama6ef0a62019-03-07 20:34:33 -0500228 // A server initiated stream is only used for a server push response,
229 // and only 200 and 30X response codes are supported for server push.
230 // This behavior mirrors the HTTP/2 implementation.
231 bool is_redirection = response_code / 100 == 3;
232 if (response_code != 200 && !is_redirection) {
233 QUIC_LOG(WARNING) << "Response to server push request " << request_url
234 << " result in response code " << response_code;
235 Reset(QUIC_STREAM_CANCELLED);
236 return;
237 }
238 }
239
240 if (!resources.empty()) {
241 QUIC_DVLOG(1) << "Stream " << id() << " found " << resources.size()
242 << " push resources.";
243 QuicSimpleServerSession* session =
244 static_cast<QuicSimpleServerSession*>(spdy_session());
fayang9a423762019-07-31 08:12:58 -0700245 session->PromisePushResources(request_url, resources, id(), precedence(),
QUICHE teama6ef0a62019-03-07 20:34:33 -0500246 request_headers_);
247 }
248
249 if (response->response_type() == QuicBackendResponse::INCOMPLETE_RESPONSE) {
250 QUIC_DVLOG(1)
251 << "Stream " << id()
252 << " sending an incomplete response, i.e. no trailer, no fin.";
253 SendIncompleteResponse(response->headers().Clone(), response->body());
254 return;
255 }
256
257 if (response->response_type() == QuicBackendResponse::STOP_SENDING) {
258 QUIC_DVLOG(1)
259 << "Stream " << id()
260 << " sending an incomplete response, i.e. no trailer, no fin.";
261 SendIncompleteResponse(response->headers().Clone(), response->body());
262 SendStopSending(response->stop_sending_code());
263 return;
264 }
265
rch7bd54762019-10-15 10:53:24 -0700266 if (response->response_type() == QuicBackendResponse::GENERATE_BYTES) {
267 QUIC_DVLOG(1) << "Stream " << id() << " sending a generate bytes response.";
268 std::string path = request_headers_[":path"].as_string().substr(1);
QUICHE team5015e2e2019-12-11 09:38:06 -0800269 if (!quiche::QuicheTextUtils::StringToUint64(path,
270 &generate_bytes_length_)) {
rch7bd54762019-10-15 10:53:24 -0700271 QUIC_LOG(ERROR) << "Path is not a number.";
272 SendNotFoundResponse();
273 return;
274 }
275 SpdyHeaderBlock headers = response->headers().Clone();
276 headers["content-length"] =
QUICHE team5015e2e2019-12-11 09:38:06 -0800277 quiche::QuicheTextUtils::Uint64ToString(generate_bytes_length_);
rch7bd54762019-10-15 10:53:24 -0700278
279 WriteHeaders(std::move(headers), false, nullptr);
280
281 WriteGeneratedBytes();
282
283 return;
284 }
285
QUICHE teama6ef0a62019-03-07 20:34:33 -0500286 QUIC_DVLOG(1) << "Stream " << id() << " sending response.";
287 SendHeadersAndBodyAndTrailers(response->headers().Clone(), response->body(),
288 response->trailers().Clone());
289}
290
rch7bd54762019-10-15 10:53:24 -0700291void QuicSimpleServerStream::OnCanWrite() {
292 QuicSpdyStream::OnCanWrite();
293 WriteGeneratedBytes();
294}
295
296void QuicSimpleServerStream::WriteGeneratedBytes() {
297 static size_t kChunkSize = 1024;
298 while (!HasBufferedData() && generate_bytes_length_ > 0) {
299 size_t len = std::min<size_t>(kChunkSize, generate_bytes_length_);
300 std::string data(len, 'a');
301 generate_bytes_length_ -= len;
302 bool fin = generate_bytes_length_ == 0;
303 WriteOrBufferBody(data, fin);
304 }
305}
306
QUICHE teama6ef0a62019-03-07 20:34:33 -0500307void QuicSimpleServerStream::SendNotFoundResponse() {
308 QUIC_DVLOG(1) << "Stream " << id() << " sending not found response.";
309 SpdyHeaderBlock headers;
310 headers[":status"] = "404";
311 headers["content-length"] =
QUICHE team5015e2e2019-12-11 09:38:06 -0800312 quiche::QuicheTextUtils::Uint64ToString(strlen(kNotFoundResponseBody));
QUICHE teama6ef0a62019-03-07 20:34:33 -0500313 SendHeadersAndBody(std::move(headers), kNotFoundResponseBody);
314}
315
316void QuicSimpleServerStream::SendErrorResponse() {
317 SendErrorResponse(0);
318}
319
320void QuicSimpleServerStream::SendErrorResponse(int resp_code) {
321 QUIC_DVLOG(1) << "Stream " << id() << " sending error response.";
322 SpdyHeaderBlock headers;
323 if (resp_code <= 0) {
324 headers[":status"] = "500";
325 } else {
QUICHE team5015e2e2019-12-11 09:38:06 -0800326 headers[":status"] = quiche::QuicheTextUtils::Uint64ToString(resp_code);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500327 }
328 headers["content-length"] =
QUICHE team5015e2e2019-12-11 09:38:06 -0800329 quiche::QuicheTextUtils::Uint64ToString(strlen(kErrorResponseBody));
QUICHE teama6ef0a62019-03-07 20:34:33 -0500330 SendHeadersAndBody(std::move(headers), kErrorResponseBody);
331}
332
333void QuicSimpleServerStream::SendIncompleteResponse(
334 SpdyHeaderBlock response_headers,
QUICHE team5015e2e2019-12-11 09:38:06 -0800335 quiche::QuicheStringPiece body) {
QUICHE teama6ef0a62019-03-07 20:34:33 -0500336 QUIC_DLOG(INFO) << "Stream " << id() << " writing headers (fin = false) : "
337 << response_headers.DebugString();
338 WriteHeaders(std::move(response_headers), /*fin=*/false, nullptr);
339
340 QUIC_DLOG(INFO) << "Stream " << id()
341 << " writing body (fin = false) with size: " << body.size();
342 if (!body.empty()) {
343 WriteOrBufferBody(body, /*fin=*/false);
344 }
345}
346
347void QuicSimpleServerStream::SendHeadersAndBody(
348 SpdyHeaderBlock response_headers,
QUICHE team5015e2e2019-12-11 09:38:06 -0800349 quiche::QuicheStringPiece body) {
QUICHE teama6ef0a62019-03-07 20:34:33 -0500350 SendHeadersAndBodyAndTrailers(std::move(response_headers), body,
351 SpdyHeaderBlock());
352}
353
354void QuicSimpleServerStream::SendHeadersAndBodyAndTrailers(
355 SpdyHeaderBlock response_headers,
QUICHE team5015e2e2019-12-11 09:38:06 -0800356 quiche::QuicheStringPiece body,
QUICHE teama6ef0a62019-03-07 20:34:33 -0500357 SpdyHeaderBlock response_trailers) {
358 // Send the headers, with a FIN if there's nothing else to send.
359 bool send_fin = (body.empty() && response_trailers.empty());
360 QUIC_DLOG(INFO) << "Stream " << id() << " writing headers (fin = " << send_fin
361 << ") : " << response_headers.DebugString();
362 WriteHeaders(std::move(response_headers), send_fin, nullptr);
363 if (send_fin) {
364 // Nothing else to send.
365 return;
366 }
367
368 // Send the body, with a FIN if there's no trailers to send.
369 send_fin = response_trailers.empty();
370 QUIC_DLOG(INFO) << "Stream " << id() << " writing body (fin = " << send_fin
371 << ") with size: " << body.size();
372 if (!body.empty() || send_fin) {
373 WriteOrBufferBody(body, send_fin);
374 }
375 if (send_fin) {
376 // Nothing else to send.
377 return;
378 }
379
380 // Send the trailers. A FIN is always sent with trailers.
381 QUIC_DLOG(INFO) << "Stream " << id() << " writing trailers (fin = true): "
382 << response_trailers.DebugString();
383 WriteTrailers(std::move(response_trailers), nullptr);
384}
385
386const char* const QuicSimpleServerStream::kErrorResponseBody = "bad";
387const char* const QuicSimpleServerStream::kNotFoundResponseBody =
388 "file not found";
389
390} // namespace quic