blob: 4e25552d03cc950f4d290c7349fcd8bedd4526ed [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"
17#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
18#include "net/third_party/quiche/src/quic/tools/quic_simple_server_session.h"
19#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
20
21using spdy::SpdyHeaderBlock;
22
23namespace quic {
24
25QuicSimpleServerStream::QuicSimpleServerStream(
26 QuicStreamId id,
27 QuicSpdySession* session,
28 StreamType type,
29 QuicSimpleServerBackend* quic_simple_server_backend)
30 : QuicSpdyServerStreamBase(id, session, type),
31 content_length_(-1),
32 quic_simple_server_backend_(quic_simple_server_backend) {}
33
34QuicSimpleServerStream::QuicSimpleServerStream(
35 PendingStream pending,
36 QuicSpdySession* session,
37 StreamType type,
38 QuicSimpleServerBackend* quic_simple_server_backend)
39 : QuicSpdyServerStreamBase(std::move(pending), session, type),
40 content_length_(-1),
41 quic_simple_server_backend_(quic_simple_server_backend) {}
42
43QuicSimpleServerStream::~QuicSimpleServerStream() {
44 quic_simple_server_backend_->CloseBackendResponseStream(this);
45}
46
47void QuicSimpleServerStream::OnInitialHeadersComplete(
48 bool fin,
49 size_t frame_len,
50 const QuicHeaderList& header_list) {
51 QuicSpdyStream::OnInitialHeadersComplete(fin, frame_len, header_list);
52 if (!SpdyUtils::CopyAndValidateHeaders(header_list, &content_length_,
53 &request_headers_)) {
54 QUIC_DVLOG(1) << "Invalid headers";
55 SendErrorResponse();
56 }
57 ConsumeHeaderList();
58}
59
60void QuicSimpleServerStream::OnTrailingHeadersComplete(
61 bool fin,
62 size_t frame_len,
63 const QuicHeaderList& header_list) {
64 QUIC_BUG << "Server does not support receiving Trailers.";
65 SendErrorResponse();
66}
67
68void QuicSimpleServerStream::OnBodyAvailable() {
69 while (HasBytesToRead()) {
70 struct iovec iov;
71 if (GetReadableRegions(&iov, 1) == 0) {
72 // No more data to read.
73 break;
74 }
75 QUIC_DVLOG(1) << "Stream " << id() << " processed " << iov.iov_len
76 << " bytes.";
77 body_.append(static_cast<char*>(iov.iov_base), iov.iov_len);
78
79 if (content_length_ >= 0 &&
80 body_.size() > static_cast<uint64_t>(content_length_)) {
81 QUIC_DVLOG(1) << "Body size (" << body_.size() << ") > content length ("
82 << content_length_ << ").";
83 SendErrorResponse();
84 return;
85 }
86 MarkConsumed(iov.iov_len);
87 }
88 if (!sequencer()->IsClosed()) {
89 sequencer()->SetUnblocked();
90 return;
91 }
92
93 // If the sequencer is closed, then all the body, including the fin, has been
94 // consumed.
95 OnFinRead();
96
97 if (write_side_closed() || fin_buffered()) {
98 return;
99 }
100
101 SendResponse();
102}
103
104void QuicSimpleServerStream::PushResponse(
105 SpdyHeaderBlock push_request_headers) {
106 if (QuicUtils::IsClientInitiatedStreamId(
107 session()->connection()->transport_version(), id())) {
108 QUIC_BUG << "Client initiated stream shouldn't be used as promised stream.";
109 return;
110 }
111 // Change the stream state to emulate a client request.
112 request_headers_ = std::move(push_request_headers);
113 content_length_ = 0;
114 QUIC_DVLOG(1) << "Stream " << id()
115 << " ready to receive server push response.";
116 DCHECK(reading_stopped());
117
118 // Directly send response based on the emulated request_headers_.
119 SendResponse();
120}
121
122void QuicSimpleServerStream::SendResponse() {
123 if (request_headers_.empty()) {
124 QUIC_DVLOG(1) << "Request headers empty.";
125 SendErrorResponse();
126 return;
127 }
128
129 if (content_length_ > 0 &&
130 static_cast<uint64_t>(content_length_) != body_.size()) {
131 QUIC_DVLOG(1) << "Content length (" << content_length_ << ") != body size ("
132 << body_.size() << ").";
133 SendErrorResponse();
134 return;
135 }
136
137 if (!QuicContainsKey(request_headers_, ":authority") ||
138 !QuicContainsKey(request_headers_, ":path")) {
139 QUIC_DVLOG(1) << "Request headers do not contain :authority or :path.";
140 SendErrorResponse();
141 return;
142 }
143
144 // Fetch the response from the backend interface and wait for callback once
145 // response is ready
146 quic_simple_server_backend_->FetchResponseFromBackend(request_headers_, body_,
147 this);
148}
149
150QuicConnectionId QuicSimpleServerStream::connection_id() const {
151 return spdy_session()->connection_id();
152}
153
154QuicStreamId QuicSimpleServerStream::stream_id() const {
155 return id();
156}
157
vasilvvc48c8712019-03-11 13:38:16 -0700158std::string QuicSimpleServerStream::peer_host() const {
QUICHE teama6ef0a62019-03-07 20:34:33 -0500159 return spdy_session()->peer_address().host().ToString();
160}
161
162void QuicSimpleServerStream::OnResponseBackendComplete(
163 const QuicBackendResponse* response,
164 std::list<QuicBackendResponse::ServerPushInfo> resources) {
165 if (response == nullptr) {
166 QUIC_DVLOG(1) << "Response not found in cache.";
167 SendNotFoundResponse();
168 return;
169 }
170
171 if (response->response_type() == QuicBackendResponse::CLOSE_CONNECTION) {
172 QUIC_DVLOG(1) << "Special response: closing connection.";
173 CloseConnectionWithDetails(QUIC_NO_ERROR, "Toy server forcing close");
174 return;
175 }
176
177 if (response->response_type() == QuicBackendResponse::IGNORE_REQUEST) {
178 QUIC_DVLOG(1) << "Special response: ignoring request.";
179 return;
180 }
181
182 if (response->response_type() == QuicBackendResponse::BACKEND_ERR_RESPONSE) {
183 QUIC_DVLOG(1) << "Quic Proxy: Backend connection error.";
184 /*502 Bad Gateway
185 The server was acting as a gateway or proxy and received an
186 invalid response from the upstream server.*/
187 SendErrorResponse(502);
188 return;
189 }
190
191 // Examing response status, if it was not pure integer as typical h2
192 // response status, send error response. Notice that
193 // QuicHttpResponseCache push urls are strictly authority + path only,
194 // scheme is not included (see |QuicHttpResponseCache::GetKey()|).
vasilvvc48c8712019-03-11 13:38:16 -0700195 std::string request_url = request_headers_[":authority"].as_string() +
196 request_headers_[":path"].as_string();
QUICHE teama6ef0a62019-03-07 20:34:33 -0500197 int response_code;
198 const SpdyHeaderBlock& response_headers = response->headers();
199 if (!ParseHeaderStatusCode(response_headers, &response_code)) {
200 auto status = response_headers.find(":status");
201 if (status == response_headers.end()) {
202 QUIC_LOG(WARNING)
203 << ":status not present in response from cache for request "
204 << request_url;
205 } else {
206 QUIC_LOG(WARNING) << "Illegal (non-integer) response :status from cache: "
207 << status->second << " for request " << request_url;
208 }
209 SendErrorResponse();
210 return;
211 }
212
213 if (QuicUtils::IsServerInitiatedStreamId(
214 session()->connection()->transport_version(), id())) {
215 // A server initiated stream is only used for a server push response,
216 // and only 200 and 30X response codes are supported for server push.
217 // This behavior mirrors the HTTP/2 implementation.
218 bool is_redirection = response_code / 100 == 3;
219 if (response_code != 200 && !is_redirection) {
220 QUIC_LOG(WARNING) << "Response to server push request " << request_url
221 << " result in response code " << response_code;
222 Reset(QUIC_STREAM_CANCELLED);
223 return;
224 }
225 }
226
227 if (!resources.empty()) {
228 QUIC_DVLOG(1) << "Stream " << id() << " found " << resources.size()
229 << " push resources.";
230 QuicSimpleServerSession* session =
231 static_cast<QuicSimpleServerSession*>(spdy_session());
232 session->PromisePushResources(request_url, resources, id(),
233 request_headers_);
234 }
235
236 if (response->response_type() == QuicBackendResponse::INCOMPLETE_RESPONSE) {
237 QUIC_DVLOG(1)
238 << "Stream " << id()
239 << " sending an incomplete response, i.e. no trailer, no fin.";
240 SendIncompleteResponse(response->headers().Clone(), response->body());
241 return;
242 }
243
244 if (response->response_type() == QuicBackendResponse::STOP_SENDING) {
245 QUIC_DVLOG(1)
246 << "Stream " << id()
247 << " sending an incomplete response, i.e. no trailer, no fin.";
248 SendIncompleteResponse(response->headers().Clone(), response->body());
249 SendStopSending(response->stop_sending_code());
250 return;
251 }
252
253 QUIC_DVLOG(1) << "Stream " << id() << " sending response.";
254 SendHeadersAndBodyAndTrailers(response->headers().Clone(), response->body(),
255 response->trailers().Clone());
256}
257
258void QuicSimpleServerStream::SendNotFoundResponse() {
259 QUIC_DVLOG(1) << "Stream " << id() << " sending not found response.";
260 SpdyHeaderBlock headers;
261 headers[":status"] = "404";
262 headers["content-length"] =
263 QuicTextUtils::Uint64ToString(strlen(kNotFoundResponseBody));
264 SendHeadersAndBody(std::move(headers), kNotFoundResponseBody);
265}
266
267void QuicSimpleServerStream::SendErrorResponse() {
268 SendErrorResponse(0);
269}
270
271void QuicSimpleServerStream::SendErrorResponse(int resp_code) {
272 QUIC_DVLOG(1) << "Stream " << id() << " sending error response.";
273 SpdyHeaderBlock headers;
274 if (resp_code <= 0) {
275 headers[":status"] = "500";
276 } else {
277 headers[":status"] = QuicTextUtils::Uint64ToString(resp_code);
278 }
279 headers["content-length"] =
280 QuicTextUtils::Uint64ToString(strlen(kErrorResponseBody));
281 SendHeadersAndBody(std::move(headers), kErrorResponseBody);
282}
283
284void QuicSimpleServerStream::SendIncompleteResponse(
285 SpdyHeaderBlock response_headers,
286 QuicStringPiece body) {
287 QUIC_DLOG(INFO) << "Stream " << id() << " writing headers (fin = false) : "
288 << response_headers.DebugString();
289 WriteHeaders(std::move(response_headers), /*fin=*/false, nullptr);
290
291 QUIC_DLOG(INFO) << "Stream " << id()
292 << " writing body (fin = false) with size: " << body.size();
293 if (!body.empty()) {
294 WriteOrBufferBody(body, /*fin=*/false);
295 }
296}
297
298void QuicSimpleServerStream::SendHeadersAndBody(
299 SpdyHeaderBlock response_headers,
300 QuicStringPiece body) {
301 SendHeadersAndBodyAndTrailers(std::move(response_headers), body,
302 SpdyHeaderBlock());
303}
304
305void QuicSimpleServerStream::SendHeadersAndBodyAndTrailers(
306 SpdyHeaderBlock response_headers,
307 QuicStringPiece body,
308 SpdyHeaderBlock response_trailers) {
309 // Send the headers, with a FIN if there's nothing else to send.
310 bool send_fin = (body.empty() && response_trailers.empty());
311 QUIC_DLOG(INFO) << "Stream " << id() << " writing headers (fin = " << send_fin
312 << ") : " << response_headers.DebugString();
313 WriteHeaders(std::move(response_headers), send_fin, nullptr);
314 if (send_fin) {
315 // Nothing else to send.
316 return;
317 }
318
319 // Send the body, with a FIN if there's no trailers to send.
320 send_fin = response_trailers.empty();
321 QUIC_DLOG(INFO) << "Stream " << id() << " writing body (fin = " << send_fin
322 << ") with size: " << body.size();
323 if (!body.empty() || send_fin) {
324 WriteOrBufferBody(body, send_fin);
325 }
326 if (send_fin) {
327 // Nothing else to send.
328 return;
329 }
330
331 // Send the trailers. A FIN is always sent with trailers.
332 QUIC_DLOG(INFO) << "Stream " << id() << " writing trailers (fin = true): "
333 << response_trailers.DebugString();
334 WriteTrailers(std::move(response_trailers), nullptr);
335}
336
337const char* const QuicSimpleServerStream::kErrorResponseBody = "bad";
338const char* const QuicSimpleServerStream::kNotFoundResponseBody =
339 "file not found";
340
341} // namespace quic