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.
4
5#include "net/tools/quic/quic_spdy_server_stream.h"
6
7#include "base/memory/singleton.h"
8#include "net/quic/quic_session.h"
9#include "net/spdy/spdy_framer.h"
10#include "net/tools/quic/quic_in_memory_cache.h"
11#include "net/tools/quic/spdy_utils.h"
12
13using base::StringPiece;
14using std::string;
15
16namespace net {
17namespace tools {
18
19static const size_t kHeaderBufInitialSize = 4096;
20
21QuicSpdyServerStream::QuicSpdyServerStream(QuicStreamId id,
22                                           QuicSession* session)
23    : QuicDataStream(id, session),
24      read_buf_(new GrowableIOBuffer()),
25      request_headers_received_(false) {
26}
27
28QuicSpdyServerStream::~QuicSpdyServerStream() {
29}
30
31uint32 QuicSpdyServerStream::ProcessData(const char* data, uint32 data_len) {
32  uint32 total_bytes_processed = 0;
33
34  // Are we still reading the request headers.
35  if (!request_headers_received_) {
36    // Grow the read buffer if necessary.
37    if (read_buf_->RemainingCapacity() < (int)data_len) {
38      read_buf_->SetCapacity(read_buf_->capacity() + kHeaderBufInitialSize);
39    }
40    memcpy(read_buf_->data(), data, data_len);
41    read_buf_->set_offset(read_buf_->offset() + data_len);
42    ParseRequestHeaders();
43  } else {
44    body_.append(data + total_bytes_processed,
45                 data_len - total_bytes_processed);
46  }
47  return data_len;
48}
49
50void QuicSpdyServerStream::OnFinRead() {
51  ReliableQuicStream::OnFinRead();
52  if (write_side_closed() || fin_buffered()) {
53    return;
54  }
55
56  if (!request_headers_received_) {
57    SendErrorResponse();  // We're not done reading headers.
58  } else if ((headers_.content_length_status() ==
59              BalsaHeadersEnums::VALID_CONTENT_LENGTH) &&
60             body_.size() != headers_.content_length()) {
61    SendErrorResponse();  // Invalid content length
62  } else {
63    SendResponse();
64  }
65}
66
67void QuicSpdyServerStream::ParseRequestHeaders() {
68  size_t read_buf_len = static_cast<size_t>(read_buf_->offset());
69  SpdyFramer framer(SPDY3);
70  SpdyHeaderBlock headers;
71  char* data = read_buf_->StartOfBuffer();
72  size_t len = framer.ParseHeaderBlockInBuffer(data, read_buf_->offset(),
73                                               &headers);
74  if (len == 0) {
75    return;
76  }
77
78  if (!SpdyUtils::FillBalsaRequestHeaders(headers, &headers_)) {
79    SendErrorResponse();
80    return;
81  }
82
83  size_t delta = read_buf_len - len;
84  if (delta > 0) {
85    body_.append(data + len, delta);
86  }
87
88  request_headers_received_ = true;
89}
90
91void QuicSpdyServerStream::SendResponse() {
92  // Find response in cache. If not found, send error response.
93  const QuicInMemoryCache::Response* response =
94      QuicInMemoryCache::GetInstance()->GetResponse(headers_);
95  if (response == NULL) {
96    SendErrorResponse();
97    return;
98  }
99
100  if (response->response_type() == QuicInMemoryCache::CLOSE_CONNECTION) {
101    DVLOG(1) << "Special response: closing connection.";
102    CloseConnection(QUIC_NO_ERROR);
103    return;
104  }
105
106  if (response->response_type() == QuicInMemoryCache::IGNORE_REQUEST) {
107    DVLOG(1) << "Special response: ignoring request.";
108    return;
109  }
110
111  DVLOG(1) << "Sending response for stream " << id();
112  SendHeadersAndBody(response->headers(), response->body());
113}
114
115void QuicSpdyServerStream::SendErrorResponse() {
116  DVLOG(1) << "Sending error response for stream " << id();
117  BalsaHeaders headers;
118  headers.SetResponseFirstlineFromStringPieces(
119      "HTTP/1.1", "500", "Server Error");
120  headers.ReplaceOrAppendHeader("content-length", "3");
121  SendHeadersAndBody(headers, "bad");
122}
123
124void QuicSpdyServerStream::SendHeadersAndBody(
125    const BalsaHeaders& response_headers,
126    StringPiece body) {
127  // We only support SPDY and HTTP, and neither handles bidirectional streaming.
128  if (!read_side_closed()) {
129    CloseReadSide();
130  }
131
132  SpdyHeaderBlock header_block =
133      SpdyUtils::ResponseHeadersToSpdyHeaders(response_headers);
134
135  WriteHeaders(header_block, body.empty(), NULL);
136
137  if (!body.empty()) {
138    WriteOrBufferData(body, true, NULL);
139  }
140}
141
142}  // namespace tools
143}  // namespace net
144