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_client_stream.h"
6
7#include "net/spdy/spdy_framer.h"
8#include "net/tools/quic/quic_client_session.h"
9#include "net/tools/quic/spdy_utils.h"
10
11using base::StringPiece;
12using std::string;
13
14namespace net {
15namespace tools {
16
17static const size_t kHeaderBufInitialSize = 4096;
18
19QuicSpdyClientStream::QuicSpdyClientStream(QuicStreamId id,
20                                           QuicClientSession* session)
21    : QuicReliableClientStream(id, session),
22      read_buf_(new GrowableIOBuffer()),
23      response_headers_received_(false) {
24}
25
26QuicSpdyClientStream::~QuicSpdyClientStream() {
27}
28
29uint32 QuicSpdyClientStream::ProcessData(const char* data, uint32 length) {
30  uint32 total_bytes_processed = 0;
31
32  // Are we still reading the response headers.
33  if (!response_headers_received_) {
34    // Grow the read buffer if necessary.
35    if (read_buf_->RemainingCapacity() < (int)length) {
36      read_buf_->SetCapacity(read_buf_->capacity() + kHeaderBufInitialSize);
37    }
38    memcpy(read_buf_->data(), data, length);
39    read_buf_->set_offset(read_buf_->offset() + length);
40    ParseResponseHeaders();
41  } else {
42    mutable_data()->append(data + total_bytes_processed,
43                           length - total_bytes_processed);
44  }
45  return length;
46}
47
48void QuicSpdyClientStream::TerminateFromPeer(bool half_close) {
49  ReliableQuicStream::TerminateFromPeer(half_close);
50  if (!response_headers_received_) {
51    Close(QUIC_BAD_APPLICATION_PAYLOAD);
52  } else if ((headers().content_length_status() ==
53             BalsaHeadersEnums::VALID_CONTENT_LENGTH) &&
54             mutable_data()->size() != headers().content_length()) {
55    Close(QUIC_BAD_APPLICATION_PAYLOAD);
56  }
57}
58
59ssize_t QuicSpdyClientStream::SendRequest(const BalsaHeaders& headers,
60                                          StringPiece body,
61                                          bool fin) {
62  SpdyHeaderBlock header_block =
63      SpdyUtils::RequestHeadersToSpdyHeaders(headers);
64
65  string headers_string =
66      session()->compressor()->CompressHeaders(header_block);
67
68  bool has_body = !body.empty();
69
70  WriteData(headers_string, fin && !has_body);  // last_data
71
72  if (has_body) {
73    WriteData(body, fin);
74  }
75
76  return headers_string.size() + body.size();
77}
78
79int QuicSpdyClientStream::ParseResponseHeaders() {
80  size_t read_buf_len = static_cast<size_t>(read_buf_->offset());
81  SpdyFramer framer(SPDY3);
82  SpdyHeaderBlock headers;
83  char* data = read_buf_->StartOfBuffer();
84  size_t len = framer.ParseHeaderBlockInBuffer(data, read_buf_->offset(),
85                                               &headers);
86  if (len == 0) {
87    return -1;
88  }
89
90  if (!SpdyUtils::FillBalsaResponseHeaders(headers, mutable_headers())) {
91    Close(QUIC_BAD_APPLICATION_PAYLOAD);
92    return -1;
93  }
94
95  size_t delta = read_buf_len - len;
96  if (delta > 0) {
97    mutable_data()->append(data + len, delta);
98  }
99
100  return len;
101}
102
103}  // namespace tools
104}  // namespace net
105