// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/quic/quic_spdy_server_stream.h" #include "base/memory/singleton.h" #include "base/strings/string_number_conversions.h" #include "net/quic/quic_in_memory_cache.h" #include "net/quic/quic_session.h" #include "net/quic/spdy_utils.h" #include "net/spdy/spdy_framer.h" #include "net/spdy/spdy_header_block.h" #include "net/spdy/spdy_http_utils.h" using base::StringPiece; using std::string; namespace net { static const size_t kHeaderBufInitialSize = 4096; QuicSpdyServerStream::QuicSpdyServerStream(QuicStreamId id, QuicSession* session) : QuicDataStream(id, session), read_buf_(new GrowableIOBuffer()), request_headers_received_(false) { read_buf_->SetCapacity(kHeaderBufInitialSize); } QuicSpdyServerStream::~QuicSpdyServerStream() { } uint32 QuicSpdyServerStream::ProcessData(const char* data, uint32 data_len) { if (data_len > INT_MAX) { LOG(DFATAL) << "Data length too long: " << data_len; return 0; } // Are we still reading the request headers. if (!request_headers_received_) { // Grow the read buffer if necessary. while (read_buf_->RemainingCapacity() < static_cast(data_len)) { read_buf_->SetCapacity(read_buf_->capacity() * 2); } memcpy(read_buf_->data(), data, data_len); read_buf_->set_offset(read_buf_->offset() + data_len); // Try parsing the request headers. This will set request_headers_received_ // if successful; if not, it will be tried again with more data. ParseRequestHeaders(); } else { body_.append(data, data_len); } return data_len; } void QuicSpdyServerStream::OnFinRead() { ReliableQuicStream::OnFinRead(); if (write_side_closed() || fin_buffered()) { return; } if (!request_headers_received_) { SendErrorResponse(); // We're not done reading headers. return; } SpdyHeaderBlock::const_iterator it = headers_.find("content-length"); size_t content_length; if (it != headers_.end() && (!base::StringToSizeT(it->second, &content_length) || body_.size() != content_length)) { SendErrorResponse(); // Invalid content length return; } SendResponse(); } // Try parsing the request headers. If successful, sets // request_headers_received_. If not successful, it can just be tried again once // there's more data. void QuicSpdyServerStream::ParseRequestHeaders() { SpdyFramer framer((kDefaultSpdyMajorVersion)); const char* data = read_buf_->StartOfBuffer(); size_t read_buf_len = static_cast(read_buf_->offset()); size_t len = framer.ParseHeaderBlockInBuffer(data, read_buf_len, &headers_); if (len == 0) { // Not enough data yet, presumably. (If we still don't succeed by the end of // the stream, then we'll error in OnFinRead().) return; } // Headers received and parsed: extract the request URL. request_url_ = GetUrlFromHeaderBlock(headers_, kDefaultSpdyMajorVersion, false); if (!request_url_.is_valid()) { SendErrorResponse(); return; } // Add any data past the headers to the request body. size_t delta = read_buf_len - len; if (delta > 0) { body_.append(data + len, delta); } request_headers_received_ = true; } void QuicSpdyServerStream::SendResponse() { // Find response in cache. If not found, send error response. const QuicInMemoryCache::Response* response = QuicInMemoryCache::GetInstance()->GetResponse(request_url_); if (response == nullptr) { SendErrorResponse(); return; } if (response->response_type() == QuicInMemoryCache::CLOSE_CONNECTION) { DVLOG(1) << "Special response: closing connection."; CloseConnection(QUIC_NO_ERROR); return; } if (response->response_type() == QuicInMemoryCache::IGNORE_REQUEST) { DVLOG(1) << "Special response: ignoring request."; return; } DVLOG(1) << "Sending response for stream " << id(); SendHeadersAndBody(response->headers(), response->body()); } void QuicSpdyServerStream::SendErrorResponse() { DVLOG(1) << "Sending error response for stream " << id(); scoped_refptr headers = new HttpResponseHeaders(string("HTTP/1.1 500 Server Error") + '\0' + "content-length: 3" + '\0' + '\0'); SendHeadersAndBody(*headers.get(), "bad"); } void QuicSpdyServerStream::SendHeadersAndBody( const HttpResponseHeaders& response_headers, StringPiece body) { // We only support SPDY and HTTP, and neither handles bidirectional streaming. if (!read_side_closed()) { CloseReadSide(); } SpdyHeaderBlock header_block; CreateSpdyHeadersFromHttpResponse(response_headers, kDefaultSpdyMajorVersion, &header_block); WriteHeaders(header_block, body.empty(), nullptr); if (!body.empty()) { WriteOrBufferData(body, true, nullptr); } } } // namespace net