// Copyright (c) 2012 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/tools/quic/quic_spdy_server_stream.h" #include "base/memory/singleton.h" #include "net/quic/quic_session.h" #include "net/spdy/spdy_framer.h" #include "net/tools/quic/quic_in_memory_cache.h" #include "net/tools/quic/spdy_utils.h" using base::StringPiece; using std::string; namespace net { namespace tools { static const size_t kHeaderBufInitialSize = 4096; QuicSpdyServerStream::QuicSpdyServerStream(QuicStreamId id, QuicSession* session) : QuicDataStream(id, session), read_buf_(new GrowableIOBuffer()), request_headers_received_(false) { } QuicSpdyServerStream::~QuicSpdyServerStream() { } uint32 QuicSpdyServerStream::ProcessData(const char* data, uint32 data_len) { uint32 total_bytes_processed = 0; // Are we still reading the request headers. if (!request_headers_received_) { // Grow the read buffer if necessary. if (read_buf_->RemainingCapacity() < (int)data_len) { read_buf_->SetCapacity(read_buf_->capacity() + kHeaderBufInitialSize); } memcpy(read_buf_->data(), data, data_len); read_buf_->set_offset(read_buf_->offset() + data_len); ParseRequestHeaders(); } else { body_.append(data + total_bytes_processed, data_len - total_bytes_processed); } 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. } else if ((headers_.content_length_status() == BalsaHeadersEnums::VALID_CONTENT_LENGTH) && body_.size() != headers_.content_length()) { SendErrorResponse(); // Invalid content length } else { SendResponse(); } } int QuicSpdyServerStream::ParseRequestHeaders() { size_t read_buf_len = static_cast(read_buf_->offset()); SpdyFramer framer(SPDY3); SpdyHeaderBlock headers; char* data = read_buf_->StartOfBuffer(); size_t len = framer.ParseHeaderBlockInBuffer(data, read_buf_->offset(), &headers); if (len == 0) { return -1; } if (!SpdyUtils::FillBalsaRequestHeaders(headers, &headers_)) { SendErrorResponse(); return -1; } size_t delta = read_buf_len - len; if (delta > 0) { body_.append(data + len, delta); } request_headers_received_ = true; return len; } void QuicSpdyServerStream::SendResponse() { // Find response in cache. If not found, send error response. const QuicInMemoryCache::Response* response = QuicInMemoryCache::GetInstance()->GetResponse(headers_); if (response == NULL) { 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(); BalsaHeaders headers; headers.SetResponseFirstlineFromStringPieces( "HTTP/1.1", "500", "Server Error"); headers.ReplaceOrAppendHeader("content-length", "3"); SendHeadersAndBody(headers, "bad"); } void QuicSpdyServerStream:: SendHeadersAndBody( const BalsaHeaders& response_headers, StringPiece body) { // We only support SPDY and HTTP, and neither handles bidirectional streaming. if (!read_side_closed()) { CloseReadSide(); } SpdyHeaderBlock header_block = SpdyUtils::ResponseHeadersToSpdyHeaders(response_headers); WriteHeaders(header_block, body.empty()); if (!body.empty()) { WriteOrBufferData(body, true); } } } // namespace tools } // namespace net