Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / net / tools / quic / quic_spdy_server_stream.cc
blobaeb56861c10c850bb54715af6078a222f434c6ef
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.
5 #include "net/tools/quic/quic_spdy_server_stream.h"
7 #include "base/logging.h"
8 #include "base/stl_util.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_piece.h"
11 #include "net/quic/quic_data_stream.h"
12 #include "net/quic/quic_spdy_session.h"
13 #include "net/quic/spdy_utils.h"
14 #include "net/spdy/spdy_protocol.h"
15 #include "net/tools/quic/quic_in_memory_cache.h"
17 using base::StringPiece;
18 using base::StringToInt;
19 using std::string;
21 namespace net {
22 namespace tools {
24 QuicSpdyServerStream::QuicSpdyServerStream(QuicStreamId id,
25 QuicSpdySession* session)
26 : QuicDataStream(id, session), content_length_(-1) {
29 QuicSpdyServerStream::~QuicSpdyServerStream() {
32 void QuicSpdyServerStream::OnStreamHeadersComplete(bool fin, size_t frame_len) {
33 QuicDataStream::OnStreamHeadersComplete(fin, frame_len);
34 if (!ParseRequestHeaders(decompressed_headers().data(),
35 decompressed_headers().length())) {
36 // Headers were invalid.
37 SendErrorResponse();
39 MarkHeadersConsumed(decompressed_headers().length());
42 void QuicSpdyServerStream::OnDataAvailable() {
43 while (HasBytesToRead()) {
44 struct iovec iov;
45 if (GetReadableRegions(&iov, 1) == 0) {
46 // No more data to read.
47 break;
49 DVLOG(1) << "Processed " << iov.iov_len << " bytes for stream " << id();
50 body_.append(static_cast<char*>(iov.iov_base), iov.iov_len);
52 if (content_length_ >= 0 &&
53 static_cast<int>(body_.size()) > content_length_) {
54 SendErrorResponse();
55 return;
57 MarkConsumed(iov.iov_len);
59 if (!sequencer()->IsClosed()) {
60 sequencer()->SetUnblocked();
61 return;
64 // If the sequencer is closed, then the all the body, including the fin,
65 // has been consumed.
66 OnFinRead();
68 if (write_side_closed() || fin_buffered()) {
69 return;
72 if (request_headers_.empty()) {
73 SendErrorResponse();
74 return;
77 if (content_length_ > 0 &&
78 content_length_ != static_cast<int>(body_.size())) {
79 SendErrorResponse();
80 return;
83 SendResponse();
86 bool QuicSpdyServerStream::ParseRequestHeaders(const char* data,
87 uint32 data_len) {
88 DCHECK(headers_decompressed());
89 SpdyFramer framer(HTTP2);
90 size_t len = framer.ParseHeaderBlockInBuffer(data,
91 data_len,
92 &request_headers_);
93 DCHECK_LE(len, data_len);
94 if (len == 0 || request_headers_.empty()) {
95 return false; // Headers were invalid.
98 if (data_len > len) {
99 body_.append(data + len, data_len - len);
101 if (ContainsKey(request_headers_, "content-length")) {
102 // Historically, if an input to SimpleAtoi contained null byte, anything
103 // past it would be silently ignored. This behavior is being removed, but
104 // this method relies on it (see cl/101239633). Hence, we explicitly call
105 // c_str() on request headers to simulate the old behavior.
106 // TODO(rch): Correctly handle null-separated value in content-length.
107 // b/23554022
108 StringPiece trimmed_header(request_headers_["content-length"].c_str());
109 if (!StringToInt(trimmed_header, &content_length_)) {
110 return false; // Invalid content-length.
113 return true;
116 void QuicSpdyServerStream::SendResponse() {
117 if (!ContainsKey(request_headers_, GetHostKey()) ||
118 !ContainsKey(request_headers_, ":path")) {
119 SendErrorResponse();
120 return;
123 // Find response in cache. If not found, send error response.
124 const QuicInMemoryCache::Response* response =
125 QuicInMemoryCache::GetInstance()->GetResponse(
126 request_headers_[GetHostKey()], request_headers_[":path"]);
127 if (response == nullptr) {
128 SendErrorResponse();
129 return;
132 if (response->response_type() == QuicInMemoryCache::CLOSE_CONNECTION) {
133 DVLOG(1) << "Special response: closing connection.";
134 CloseConnection(QUIC_NO_ERROR);
135 return;
138 if (response->response_type() == QuicInMemoryCache::IGNORE_REQUEST) {
139 DVLOG(1) << "Special response: ignoring request.";
140 return;
143 DVLOG(1) << "Sending response for stream " << id();
144 if (version() > QUIC_VERSION_24) {
145 SendHeadersAndBody(
146 SpdyUtils::ConvertSpdy3ResponseHeadersToSpdy4(response->headers()),
147 response->body());
148 return;
151 SendHeadersAndBody(response->headers(), response->body());
154 void QuicSpdyServerStream::SendErrorResponse() {
155 DVLOG(1) << "Sending error response for stream " << id();
156 SpdyHeaderBlock headers;
157 if (version() <= QUIC_VERSION_24) {
158 headers[":version"] = "HTTP/1.1";
159 headers[":status"] = "500 Server Error";
160 } else {
161 headers[":status"] = "500";
163 headers["content-length"] = "3";
164 SendHeadersAndBody(headers, "bad");
167 void QuicSpdyServerStream::SendHeadersAndBody(
168 const SpdyHeaderBlock& response_headers,
169 StringPiece body) {
170 // We only support SPDY and HTTP, and neither handles bidirectional streaming.
171 if (!read_side_closed()) {
172 CloseReadSide();
175 WriteHeaders(response_headers, body.empty(), nullptr);
177 if (!body.empty()) {
178 WriteOrBufferData(body, true, nullptr);
182 const string QuicSpdyServerStream::GetHostKey() {
183 // SPDY/4 uses ":authority" instead of ":host".
184 return version() > QUIC_VERSION_24 ? ":authority" : ":host";
187 } // namespace tools
188 } // namespace net