xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/tools/quic_simple_server_stream.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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 "quiche/quic/tools/quic_simple_server_stream.h"
6 
7 #include <cstdint>
8 #include <list>
9 #include <optional>
10 #include <utility>
11 
12 #include "absl/strings/numbers.h"
13 #include "absl/strings/str_cat.h"
14 #include "absl/strings/string_view.h"
15 #include "quiche/quic/core/http/quic_spdy_stream.h"
16 #include "quiche/quic/core/http/spdy_utils.h"
17 #include "quiche/quic/core/http/web_transport_http3.h"
18 #include "quiche/quic/core/quic_error_codes.h"
19 #include "quiche/quic/core/quic_utils.h"
20 #include "quiche/quic/platform/api/quic_bug_tracker.h"
21 #include "quiche/quic/platform/api/quic_flags.h"
22 #include "quiche/quic/platform/api/quic_logging.h"
23 #include "quiche/quic/tools/quic_simple_server_session.h"
24 #include "quiche/spdy/core/spdy_protocol.h"
25 
26 using spdy::Http2HeaderBlock;
27 
28 namespace quic {
29 
QuicSimpleServerStream(QuicStreamId id,QuicSpdySession * session,StreamType type,QuicSimpleServerBackend * quic_simple_server_backend)30 QuicSimpleServerStream::QuicSimpleServerStream(
31     QuicStreamId id, QuicSpdySession* session, StreamType type,
32     QuicSimpleServerBackend* quic_simple_server_backend)
33     : QuicSpdyServerStreamBase(id, session, type),
34       content_length_(-1),
35       generate_bytes_length_(0),
36       quic_simple_server_backend_(quic_simple_server_backend) {
37   QUICHE_DCHECK(quic_simple_server_backend_);
38 }
39 
QuicSimpleServerStream(PendingStream * pending,QuicSpdySession * session,QuicSimpleServerBackend * quic_simple_server_backend)40 QuicSimpleServerStream::QuicSimpleServerStream(
41     PendingStream* pending, QuicSpdySession* session,
42     QuicSimpleServerBackend* quic_simple_server_backend)
43     : QuicSpdyServerStreamBase(pending, session),
44       content_length_(-1),
45       generate_bytes_length_(0),
46       quic_simple_server_backend_(quic_simple_server_backend) {
47   QUICHE_DCHECK(quic_simple_server_backend_);
48 }
49 
~QuicSimpleServerStream()50 QuicSimpleServerStream::~QuicSimpleServerStream() {
51   quic_simple_server_backend_->CloseBackendResponseStream(this);
52 }
53 
OnInitialHeadersComplete(bool fin,size_t frame_len,const QuicHeaderList & header_list)54 void QuicSimpleServerStream::OnInitialHeadersComplete(
55     bool fin, size_t frame_len, const QuicHeaderList& header_list) {
56   QuicSpdyStream::OnInitialHeadersComplete(fin, frame_len, header_list);
57   // QuicSpdyStream::OnInitialHeadersComplete() may have already sent error
58   // response.
59   if (!response_sent_ &&
60       !SpdyUtils::CopyAndValidateHeaders(header_list, &content_length_,
61                                          &request_headers_)) {
62     QUIC_DVLOG(1) << "Invalid headers";
63     SendErrorResponse();
64   }
65   ConsumeHeaderList();
66 
67   // CONNECT requests do not carry any message content but carry data after the
68   // headers, so they require sending the response right after parsing the
69   // headers even though the FIN bit has not been received on the request
70   // stream.
71   if (!fin && !response_sent_ && IsConnectRequest()) {
72     if (quic_simple_server_backend_ == nullptr) {
73       QUIC_DVLOG(1) << "Backend is missing on CONNECT headers.";
74       SendErrorResponse();
75       return;
76     }
77 
78     if (web_transport() != nullptr) {
79       QuicSimpleServerBackend::WebTransportResponse response =
80           quic_simple_server_backend_->ProcessWebTransportRequest(
81               request_headers_, web_transport());
82       if (response.response_headers[":status"] == "200") {
83         WriteHeaders(std::move(response.response_headers), false, nullptr);
84         if (response.visitor != nullptr) {
85           web_transport()->SetVisitor(std::move(response.visitor));
86         }
87         web_transport()->HeadersReceived(request_headers_);
88       } else {
89         WriteHeaders(std::move(response.response_headers), true, nullptr);
90       }
91       return;
92     }
93 
94     quic_simple_server_backend_->HandleConnectHeaders(request_headers_,
95                                                       /*request_handler=*/this);
96   }
97 }
98 
OnBodyAvailable()99 void QuicSimpleServerStream::OnBodyAvailable() {
100   while (HasBytesToRead()) {
101     struct iovec iov;
102     if (GetReadableRegions(&iov, 1) == 0) {
103       // No more data to read.
104       break;
105     }
106     QUIC_DVLOG(1) << "Stream " << id() << " processed " << iov.iov_len
107                   << " bytes.";
108     body_.append(static_cast<char*>(iov.iov_base), iov.iov_len);
109 
110     if (content_length_ >= 0 &&
111         body_.size() > static_cast<uint64_t>(content_length_)) {
112       QUIC_DVLOG(1) << "Body size (" << body_.size() << ") > content length ("
113                     << content_length_ << ").";
114       SendErrorResponse();
115       return;
116     }
117     MarkConsumed(iov.iov_len);
118   }
119 
120   if (!sequencer()->IsClosed()) {
121     if (IsConnectRequest()) {
122       HandleRequestConnectData(/*fin_received=*/false);
123     }
124     sequencer()->SetUnblocked();
125     return;
126   }
127 
128   // If the sequencer is closed, then all the body, including the fin, has been
129   // consumed.
130   OnFinRead();
131 
132   if (write_side_closed() || fin_buffered()) {
133     return;
134   }
135 
136   if (IsConnectRequest()) {
137     HandleRequestConnectData(/*fin_received=*/true);
138   } else {
139     SendResponse();
140   }
141 }
142 
HandleRequestConnectData(bool fin_received)143 void QuicSimpleServerStream::HandleRequestConnectData(bool fin_received) {
144   QUICHE_DCHECK(IsConnectRequest());
145 
146   if (quic_simple_server_backend_ == nullptr) {
147     QUIC_DVLOG(1) << "Backend is missing on CONNECT data.";
148     ResetWriteSide(
149         QuicResetStreamError::FromInternal(QUIC_STREAM_CONNECT_ERROR));
150     return;
151   }
152 
153   // Clear `body_`, so only new data is sent to the backend next time.
154   std::string data = std::move(body_);
155   body_.clear();
156 
157   quic_simple_server_backend_->HandleConnectData(data,
158                                                  /*data_complete=*/fin_received,
159                                                  this);
160 }
161 
SendResponse()162 void QuicSimpleServerStream::SendResponse() {
163   QUICHE_DCHECK(!IsConnectRequest());
164 
165   if (request_headers_.empty()) {
166     QUIC_DVLOG(1) << "Request headers empty.";
167     SendErrorResponse();
168     return;
169   }
170 
171   if (content_length_ > 0 &&
172       static_cast<uint64_t>(content_length_) != body_.size()) {
173     QUIC_DVLOG(1) << "Content length (" << content_length_ << ") != body size ("
174                   << body_.size() << ").";
175     SendErrorResponse();
176     return;
177   }
178 
179   if (!request_headers_.contains(":authority")) {
180     QUIC_DVLOG(1) << "Request headers do not contain :authority.";
181     SendErrorResponse();
182     return;
183   }
184 
185   if (!request_headers_.contains(":path")) {
186     QUIC_DVLOG(1) << "Request headers do not contain :path.";
187     SendErrorResponse();
188     return;
189   }
190 
191   if (quic_simple_server_backend_ == nullptr) {
192     QUIC_DVLOG(1) << "Backend is missing in SendResponse().";
193     SendErrorResponse();
194     return;
195   }
196 
197   if (web_transport() != nullptr) {
198     QuicSimpleServerBackend::WebTransportResponse response =
199         quic_simple_server_backend_->ProcessWebTransportRequest(
200             request_headers_, web_transport());
201     if (response.response_headers[":status"] == "200") {
202       WriteHeaders(std::move(response.response_headers), false, nullptr);
203       if (response.visitor != nullptr) {
204         web_transport()->SetVisitor(std::move(response.visitor));
205       }
206       web_transport()->HeadersReceived(request_headers_);
207     } else {
208       WriteHeaders(std::move(response.response_headers), true, nullptr);
209     }
210     return;
211   }
212 
213   // Fetch the response from the backend interface and wait for callback once
214   // response is ready
215   quic_simple_server_backend_->FetchResponseFromBackend(request_headers_, body_,
216                                                         this);
217 }
218 
connection_id() const219 QuicConnectionId QuicSimpleServerStream::connection_id() const {
220   return spdy_session()->connection_id();
221 }
222 
stream_id() const223 QuicStreamId QuicSimpleServerStream::stream_id() const { return id(); }
224 
peer_host() const225 std::string QuicSimpleServerStream::peer_host() const {
226   return spdy_session()->peer_address().host().ToString();
227 }
228 
GetStream()229 QuicSpdyStream* QuicSimpleServerStream::GetStream() { return this; }
230 
231 namespace {
232 
233 class DelayedResponseAlarm : public QuicAlarm::DelegateWithContext {
234  public:
DelayedResponseAlarm(QuicSimpleServerStream * stream,const QuicBackendResponse * response)235   DelayedResponseAlarm(QuicSimpleServerStream* stream,
236                        const QuicBackendResponse* response)
237       : QuicAlarm::DelegateWithContext(
238             stream->spdy_session()->connection()->context()),
239         stream_(stream),
240         response_(response) {
241     stream_ = stream;
242     response_ = response;
243   }
244 
245   ~DelayedResponseAlarm() override = default;
246 
OnAlarm()247   void OnAlarm() override { stream_->Respond(response_); }
248 
249  private:
250   QuicSimpleServerStream* stream_;
251   const QuicBackendResponse* response_;
252 };
253 
254 }  // namespace
255 
OnResponseBackendComplete(const QuicBackendResponse * response)256 void QuicSimpleServerStream::OnResponseBackendComplete(
257     const QuicBackendResponse* response) {
258   if (response == nullptr) {
259     QUIC_DVLOG(1) << "Response not found in cache.";
260     SendNotFoundResponse();
261     return;
262   }
263 
264   auto delay = response->delay();
265   if (delay.IsZero()) {
266     Respond(response);
267     return;
268   }
269 
270   auto* connection = session()->connection();
271   delayed_response_alarm_.reset(connection->alarm_factory()->CreateAlarm(
272       new DelayedResponseAlarm(this, response)));
273   delayed_response_alarm_->Set(connection->clock()->Now() + delay);
274 }
275 
Respond(const QuicBackendResponse * response)276 void QuicSimpleServerStream::Respond(const QuicBackendResponse* response) {
277   // Send Early Hints first.
278   for (const auto& headers : response->early_hints()) {
279     QUIC_DVLOG(1) << "Stream " << id() << " sending an Early Hints response: "
280                   << headers.DebugString();
281     WriteHeaders(headers.Clone(), false, nullptr);
282   }
283 
284   if (response->response_type() == QuicBackendResponse::CLOSE_CONNECTION) {
285     QUIC_DVLOG(1) << "Special response: closing connection.";
286     OnUnrecoverableError(QUIC_NO_ERROR, "Toy server forcing close");
287     return;
288   }
289 
290   if (response->response_type() == QuicBackendResponse::IGNORE_REQUEST) {
291     QUIC_DVLOG(1) << "Special response: ignoring request.";
292     return;
293   }
294 
295   if (response->response_type() == QuicBackendResponse::BACKEND_ERR_RESPONSE) {
296     QUIC_DVLOG(1) << "Quic Proxy: Backend connection error.";
297     /*502 Bad Gateway
298       The server was acting as a gateway or proxy and received an
299       invalid response from the upstream server.*/
300     SendErrorResponse(502);
301     return;
302   }
303 
304   // Examing response status, if it was not pure integer as typical h2
305   // response status, send error response. Notice that
306   // QuicHttpResponseCache push urls are strictly authority + path only,
307   // scheme is not included (see |QuicHttpResponseCache::GetKey()|).
308   std::string request_url = request_headers_[":authority"].as_string() +
309                             request_headers_[":path"].as_string();
310   int response_code;
311   const Http2HeaderBlock& response_headers = response->headers();
312   if (!ParseHeaderStatusCode(response_headers, &response_code)) {
313     auto status = response_headers.find(":status");
314     if (status == response_headers.end()) {
315       QUIC_LOG(WARNING)
316           << ":status not present in response from cache for request "
317           << request_url;
318     } else {
319       QUIC_LOG(WARNING) << "Illegal (non-integer) response :status from cache: "
320                         << status->second << " for request " << request_url;
321     }
322     SendErrorResponse();
323     return;
324   }
325 
326   if (response->response_type() == QuicBackendResponse::INCOMPLETE_RESPONSE) {
327     QUIC_DVLOG(1)
328         << "Stream " << id()
329         << " sending an incomplete response, i.e. no trailer, no fin.";
330     SendIncompleteResponse(response->headers().Clone(), response->body());
331     return;
332   }
333 
334   if (response->response_type() == QuicBackendResponse::GENERATE_BYTES) {
335     QUIC_DVLOG(1) << "Stream " << id() << " sending a generate bytes response.";
336     std::string path = request_headers_[":path"].as_string().substr(1);
337     if (!absl::SimpleAtoi(path, &generate_bytes_length_)) {
338       QUIC_LOG(ERROR) << "Path is not a number.";
339       SendNotFoundResponse();
340       return;
341     }
342     Http2HeaderBlock headers = response->headers().Clone();
343     headers["content-length"] = absl::StrCat(generate_bytes_length_);
344 
345     WriteHeaders(std::move(headers), false, nullptr);
346     QUICHE_DCHECK(!response_sent_);
347     response_sent_ = true;
348 
349     WriteGeneratedBytes();
350 
351     return;
352   }
353 
354   QUIC_DVLOG(1) << "Stream " << id() << " sending response.";
355   SendHeadersAndBodyAndTrailers(response->headers().Clone(), response->body(),
356                                 response->trailers().Clone());
357 }
358 
SendStreamData(absl::string_view data,bool close_stream)359 void QuicSimpleServerStream::SendStreamData(absl::string_view data,
360                                             bool close_stream) {
361   // Doesn't make sense to call this without data or `close_stream`.
362   QUICHE_DCHECK(!data.empty() || close_stream);
363 
364   if (close_stream) {
365     SendHeadersAndBodyAndTrailers(
366         /*response_headers=*/std::nullopt, data,
367         /*response_trailers=*/spdy::Http2HeaderBlock());
368   } else {
369     SendIncompleteResponse(/*response_headers=*/std::nullopt, data);
370   }
371 }
372 
TerminateStreamWithError(QuicResetStreamError error)373 void QuicSimpleServerStream::TerminateStreamWithError(
374     QuicResetStreamError error) {
375   QUIC_DVLOG(1) << "Stream " << id() << " abruptly terminating with error "
376                 << error.internal_code();
377   ResetWriteSide(error);
378 }
379 
OnCanWrite()380 void QuicSimpleServerStream::OnCanWrite() {
381   QuicSpdyStream::OnCanWrite();
382   WriteGeneratedBytes();
383 }
384 
WriteGeneratedBytes()385 void QuicSimpleServerStream::WriteGeneratedBytes() {
386   static size_t kChunkSize = 1024;
387   while (!HasBufferedData() && generate_bytes_length_ > 0) {
388     size_t len = std::min<size_t>(kChunkSize, generate_bytes_length_);
389     std::string data(len, 'a');
390     generate_bytes_length_ -= len;
391     bool fin = generate_bytes_length_ == 0;
392     WriteOrBufferBody(data, fin);
393   }
394 }
395 
SendNotFoundResponse()396 void QuicSimpleServerStream::SendNotFoundResponse() {
397   QUIC_DVLOG(1) << "Stream " << id() << " sending not found response.";
398   Http2HeaderBlock headers;
399   headers[":status"] = "404";
400   headers["content-length"] = absl::StrCat(strlen(kNotFoundResponseBody));
401   SendHeadersAndBody(std::move(headers), kNotFoundResponseBody);
402 }
403 
SendErrorResponse()404 void QuicSimpleServerStream::SendErrorResponse() { SendErrorResponse(0); }
405 
SendErrorResponse(int resp_code)406 void QuicSimpleServerStream::SendErrorResponse(int resp_code) {
407   QUIC_DVLOG(1) << "Stream " << id() << " sending error response.";
408   if (!reading_stopped()) {
409     StopReading();
410   }
411   Http2HeaderBlock headers;
412   if (resp_code <= 0) {
413     headers[":status"] = "500";
414   } else {
415     headers[":status"] = absl::StrCat(resp_code);
416   }
417   headers["content-length"] = absl::StrCat(strlen(kErrorResponseBody));
418   SendHeadersAndBody(std::move(headers), kErrorResponseBody);
419 }
420 
SendIncompleteResponse(std::optional<Http2HeaderBlock> response_headers,absl::string_view body)421 void QuicSimpleServerStream::SendIncompleteResponse(
422     std::optional<Http2HeaderBlock> response_headers, absl::string_view body) {
423   // Headers should be sent iff not sent in a previous response.
424   QUICHE_DCHECK_NE(response_headers.has_value(), response_sent_);
425 
426   if (response_headers.has_value()) {
427     QUIC_DLOG(INFO) << "Stream " << id() << " writing headers (fin = false) : "
428                     << response_headers.value().DebugString();
429     // Do not mark response sent for early 100 continue response.
430     int response_code;
431     if (!ParseHeaderStatusCode(*response_headers, &response_code) ||
432         response_code != 100) {
433       response_sent_ = true;
434     }
435     WriteHeaders(std::move(response_headers).value(), /*fin=*/false, nullptr);
436   }
437 
438   QUIC_DLOG(INFO) << "Stream " << id()
439                   << " writing body (fin = false) with size: " << body.size();
440   if (!body.empty()) {
441     WriteOrBufferBody(body, /*fin=*/false);
442   }
443 }
444 
SendHeadersAndBody(Http2HeaderBlock response_headers,absl::string_view body)445 void QuicSimpleServerStream::SendHeadersAndBody(
446     Http2HeaderBlock response_headers, absl::string_view body) {
447   SendHeadersAndBodyAndTrailers(std::move(response_headers), body,
448                                 Http2HeaderBlock());
449 }
450 
SendHeadersAndBodyAndTrailers(std::optional<Http2HeaderBlock> response_headers,absl::string_view body,Http2HeaderBlock response_trailers)451 void QuicSimpleServerStream::SendHeadersAndBodyAndTrailers(
452     std::optional<Http2HeaderBlock> response_headers, absl::string_view body,
453     Http2HeaderBlock response_trailers) {
454   // Headers should be sent iff not sent in a previous response.
455   QUICHE_DCHECK_NE(response_headers.has_value(), response_sent_);
456 
457   if (response_headers.has_value()) {
458     // Send the headers, with a FIN if there's nothing else to send.
459     bool send_fin = (body.empty() && response_trailers.empty());
460     QUIC_DLOG(INFO) << "Stream " << id()
461                     << " writing headers (fin = " << send_fin
462                     << ") : " << response_headers.value().DebugString();
463     WriteHeaders(std::move(response_headers).value(), send_fin, nullptr);
464     response_sent_ = true;
465     if (send_fin) {
466       // Nothing else to send.
467       return;
468     }
469   }
470 
471   // Send the body, with a FIN if there's no trailers to send.
472   bool send_fin = response_trailers.empty();
473   QUIC_DLOG(INFO) << "Stream " << id() << " writing body (fin = " << send_fin
474                   << ") with size: " << body.size();
475   if (!body.empty() || send_fin) {
476     WriteOrBufferBody(body, send_fin);
477   }
478   if (send_fin) {
479     // Nothing else to send.
480     return;
481   }
482 
483   // Send the trailers. A FIN is always sent with trailers.
484   QUIC_DLOG(INFO) << "Stream " << id() << " writing trailers (fin = true): "
485                   << response_trailers.DebugString();
486   WriteTrailers(std::move(response_trailers), nullptr);
487 }
488 
IsConnectRequest() const489 bool QuicSimpleServerStream::IsConnectRequest() const {
490   auto method_it = request_headers_.find(":method");
491   return method_it != request_headers_.end() && method_it->second == "CONNECT";
492 }
493 
OnInvalidHeaders()494 void QuicSimpleServerStream::OnInvalidHeaders() {
495   QUIC_DVLOG(1) << "Invalid headers";
496   SendErrorResponse(400);
497 }
498 
499 const char* const QuicSimpleServerStream::kErrorResponseBody = "bad";
500 const char* const QuicSimpleServerStream::kNotFoundResponseBody =
501     "file not found";
502 
503 }  // namespace quic
504