| // 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 "net/quic/quic_in_memory_cache.h" |
| #include "net/quic/quic_session.h" |
| #include "net/spdy/spdy_framer.h" |
| #include "net/tools/quic/spdy_utils.h" |
| |
| using base::StringPiece; |
| using std::string; |
| using net::tools::SpdyUtils; |
| |
| 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) { |
| } |
| |
| 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<size_t>(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(), NULL); |
| |
| if (!body.empty()) { |
| WriteOrBufferData(body, true, NULL); |
| } |
| } |
| |
| } // namespace net |