1// Copyright 2013 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/quic/quic_headers_stream.h"
6
7#include "net/quic/quic_utils.h"
8#include "net/quic/spdy_utils.h"
9#include "net/quic/test_tools/quic_connection_peer.h"
10#include "net/quic/test_tools/quic_session_peer.h"
11#include "net/quic/test_tools/quic_test_utils.h"
12#include "net/quic/test_tools/reliable_quic_stream_peer.h"
13#include "net/spdy/spdy_protocol.h"
14#include "testing/gtest/include/gtest/gtest.h"
15
16using base::StringPiece;
17using std::string;
18using testing::Invoke;
19using testing::StrictMock;
20using testing::WithArgs;
21using testing::_;
22
23namespace net {
24namespace test {
25namespace {
26
27class MockVisitor : public SpdyFramerVisitorInterface {
28 public:
29  MOCK_METHOD1(OnError, void(SpdyFramer* framer));
30  MOCK_METHOD3(OnDataFrameHeader, void(SpdyStreamId stream_id,
31                                       size_t length,
32                                       bool fin));
33  MOCK_METHOD4(OnStreamFrameData, void(SpdyStreamId stream_id,
34                                       const char* data,
35                                       size_t len,
36                                       bool fin));
37  MOCK_METHOD3(OnControlFrameHeaderData, bool(SpdyStreamId stream_id,
38                                              const char* header_data,
39                                              size_t len));
40  MOCK_METHOD5(OnSynStream, void(SpdyStreamId stream_id,
41                                 SpdyStreamId associated_stream_id,
42                                 SpdyPriority priority,
43                                 bool fin,
44                                 bool unidirectional));
45  MOCK_METHOD2(OnSynReply, void(SpdyStreamId stream_id, bool fin));
46  MOCK_METHOD2(OnRstStream, void(SpdyStreamId stream_id,
47                                 SpdyRstStreamStatus status));
48  MOCK_METHOD1(OnSettings, void(bool clear_persisted));
49  MOCK_METHOD3(OnSetting, void(SpdySettingsIds id, uint8 flags, uint32 value));
50  MOCK_METHOD0(OnSettingsAck, void());
51  MOCK_METHOD0(OnSettingsEnd, void());
52  MOCK_METHOD2(OnPing, void(SpdyPingId unique_id, bool is_ack));
53  MOCK_METHOD2(OnGoAway, void(SpdyStreamId last_accepted_stream_id,
54                              SpdyGoAwayStatus status));
55  MOCK_METHOD3(OnHeaders, void(SpdyStreamId stream_id, bool fin, bool end));
56  MOCK_METHOD2(OnWindowUpdate, void(SpdyStreamId stream_id,
57                                    uint32 delta_window_size));
58  MOCK_METHOD2(OnCredentialFrameData, bool(const char* credential_data,
59                                           size_t len));
60  MOCK_METHOD1(OnBlocked, void(SpdyStreamId stream_id));
61  MOCK_METHOD3(OnPushPromise, void(SpdyStreamId stream_id,
62                                   SpdyStreamId promised_stream_id,
63                                   bool end));
64  MOCK_METHOD2(OnContinuation, void(SpdyStreamId stream_id, bool end));
65  MOCK_METHOD6(OnAltSvc, void(SpdyStreamId stream_id,
66                              uint32 max_age,
67                              uint16 port,
68                              StringPiece protocol_id,
69                              StringPiece host,
70                              StringPiece origin));
71  MOCK_METHOD2(OnUnknownFrame, bool(SpdyStreamId stream_id, int frame_type));
72};
73
74class QuicHeadersStreamTest : public ::testing::TestWithParam<bool> {
75 public:
76  static QuicVersionVector GetVersions() {
77    QuicVersionVector versions;
78    versions.push_back(QuicVersionMax());
79    return versions;
80  }
81
82  QuicHeadersStreamTest()
83      : connection_(new StrictMock<MockConnection>(is_server(), GetVersions())),
84        session_(connection_),
85        headers_stream_(QuicSessionPeer::GetHeadersStream(&session_)),
86        body_("hello world"),
87        framer_(SPDY3) {
88    headers_[":version"]  = "HTTP/1.1";
89    headers_[":status"] = "200 Ok";
90    headers_["content-length"] = "11";
91    framer_.set_visitor(&visitor_);
92    EXPECT_EQ(QuicVersionMax(), session_.connection()->version());
93    EXPECT_TRUE(headers_stream_ != NULL);
94  }
95
96  QuicConsumedData SaveIov(const IOVector& data) {
97    const iovec* iov = data.iovec();
98    int count = data.Capacity();
99    for (int i = 0 ; i < count; ++i) {
100      saved_data_.append(static_cast<char*>(iov[i].iov_base), iov[i].iov_len);
101    }
102    return QuicConsumedData(saved_data_.length(), false);
103  }
104
105  bool SaveHeaderData(const char* data, int len) {
106    saved_header_data_.append(data, len);
107    return true;
108  }
109
110  void SaveHeaderDataStringPiece(StringPiece data) {
111    saved_header_data_.append(data.data(), data.length());
112  }
113
114  void WriteHeadersAndExpectSynStream(QuicStreamId stream_id,
115                                      bool fin,
116                                      QuicPriority priority) {
117    WriteHeadersAndCheckData(stream_id, fin, priority, SYN_STREAM);
118  }
119
120  void WriteHeadersAndExpectSynReply(QuicStreamId stream_id,
121                                     bool fin) {
122    WriteHeadersAndCheckData(stream_id, fin, 0, SYN_REPLY);
123  }
124
125  void WriteHeadersAndCheckData(QuicStreamId stream_id,
126                                bool fin,
127                                QuicPriority priority,
128                                SpdyFrameType type) {
129    // Write the headers and capture the outgoing data
130    EXPECT_CALL(session_, WritevData(kHeadersStreamId, _, _, false, _, NULL))
131        .WillOnce(WithArgs<1>(Invoke(this, &QuicHeadersStreamTest::SaveIov)));
132    headers_stream_->WriteHeaders(stream_id, headers_, fin, NULL);
133
134    // Parse the outgoing data and check that it matches was was written.
135    if (type == SYN_STREAM) {
136      EXPECT_CALL(visitor_, OnSynStream(stream_id, kNoAssociatedStream, 0,
137                                        // priority,
138                                        fin, kNotUnidirectional));
139    } else {
140      EXPECT_CALL(visitor_, OnSynReply(stream_id, fin));
141    }
142    EXPECT_CALL(visitor_, OnControlFrameHeaderData(stream_id, _, _))
143        .WillRepeatedly(WithArgs<1, 2>(
144            Invoke(this, &QuicHeadersStreamTest::SaveHeaderData)));
145    if (fin) {
146      EXPECT_CALL(visitor_, OnStreamFrameData(stream_id, NULL, 0, true));
147    }
148    framer_.ProcessInput(saved_data_.data(), saved_data_.length());
149    EXPECT_FALSE(framer_.HasError()) << framer_.error_code();
150
151    CheckHeaders();
152    saved_data_.clear();
153  }
154
155  void CheckHeaders() {
156    SpdyHeaderBlock headers;
157    EXPECT_EQ(saved_header_data_.length(),
158              framer_.ParseHeaderBlockInBuffer(saved_header_data_.data(),
159                                               saved_header_data_.length(),
160                                               &headers));
161    EXPECT_EQ(headers_, headers);
162    saved_header_data_.clear();
163  }
164
165  bool is_server() {
166    return GetParam();
167  }
168
169  void CloseConnection() {
170    QuicConnectionPeer::CloseConnection(connection_);
171  }
172
173  static const bool kNotUnidirectional = false;
174  static const bool kNoAssociatedStream = false;
175
176  StrictMock<MockConnection>* connection_;
177  StrictMock<MockSession> session_;
178  QuicHeadersStream* headers_stream_;
179  SpdyHeaderBlock headers_;
180  string body_;
181  string saved_data_;
182  string saved_header_data_;
183  SpdyFramer framer_;
184  StrictMock<MockVisitor> visitor_;
185};
186
187INSTANTIATE_TEST_CASE_P(Tests, QuicHeadersStreamTest, testing::Bool());
188
189TEST_P(QuicHeadersStreamTest, StreamId) {
190  EXPECT_EQ(3u, headers_stream_->id());
191}
192
193TEST_P(QuicHeadersStreamTest, EffectivePriority) {
194  EXPECT_EQ(0u, headers_stream_->EffectivePriority());
195}
196
197TEST_P(QuicHeadersStreamTest, WriteHeaders) {
198  for (QuicStreamId stream_id = kClientDataStreamId1;
199       stream_id < kClientDataStreamId3; stream_id += 2) {
200    for (int count = 0; count < 2; ++count) {
201      bool fin = (count == 0);
202      if (is_server()) {
203        WriteHeadersAndExpectSynReply(stream_id, fin);
204      } else {
205        for (QuicPriority priority = 0; priority < 7; ++priority) {
206          WriteHeadersAndExpectSynStream(stream_id, fin, priority);
207        }
208      }
209    }
210  }
211}
212
213TEST_P(QuicHeadersStreamTest, ProcessRawData) {
214  for (QuicStreamId stream_id = kClientDataStreamId1;
215       stream_id < kClientDataStreamId3; stream_id += 2) {
216    for (int count = 0; count < 2; ++count) {
217      bool fin = (count == 0);
218      for (QuicPriority priority = 0; priority < 7; ++priority) {
219        // Replace with "WriteHeadersAndSaveData"
220        scoped_ptr<SpdySerializedFrame> frame;
221        if (is_server()) {
222          SpdySynStreamIR syn_stream(stream_id);
223          syn_stream.set_name_value_block(headers_);
224          syn_stream.set_fin(fin);
225          frame.reset(framer_.SerializeSynStream(syn_stream));
226          EXPECT_CALL(session_, OnStreamHeadersPriority(stream_id, 0));
227        } else {
228          SpdySynReplyIR syn_reply(stream_id);
229          syn_reply.set_name_value_block(headers_);
230          syn_reply.set_fin(fin);
231          frame.reset(framer_.SerializeSynReply(syn_reply));
232        }
233        EXPECT_CALL(session_, OnStreamHeaders(stream_id, _))
234            .WillRepeatedly(WithArgs<1>(
235                Invoke(this,
236                       &QuicHeadersStreamTest::SaveHeaderDataStringPiece)));
237        EXPECT_CALL(session_,
238                    OnStreamHeadersComplete(stream_id, fin, frame->size()));
239        headers_stream_->ProcessRawData(frame->data(), frame->size());
240
241        CheckHeaders();
242      }
243    }
244  }
245}
246
247TEST_P(QuicHeadersStreamTest, ProcessSpdyDataFrame) {
248  SpdyDataIR data(2, "");
249  scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
250  EXPECT_CALL(*connection_,
251              SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
252                                             "SPDY DATA frame received."))
253      .WillOnce(InvokeWithoutArgs(this,
254                                  &QuicHeadersStreamTest::CloseConnection));
255  headers_stream_->ProcessRawData(frame->data(), frame->size());
256}
257
258TEST_P(QuicHeadersStreamTest, ProcessSpdyRstStreamFrame) {
259  SpdyRstStreamIR data(2, RST_STREAM_PROTOCOL_ERROR, "");
260  scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
261  EXPECT_CALL(*connection_,
262              SendConnectionCloseWithDetails(
263                  QUIC_INVALID_HEADERS_STREAM_DATA,
264                  "SPDY RST_STREAM frame received."))
265      .WillOnce(InvokeWithoutArgs(this,
266                                  &QuicHeadersStreamTest::CloseConnection));
267  headers_stream_->ProcessRawData(frame->data(), frame->size());
268}
269
270TEST_P(QuicHeadersStreamTest, ProcessSpdySettingsFrame) {
271  SpdySettingsIR data;
272  data.AddSetting(SETTINGS_UPLOAD_BANDWIDTH, true, true, 0);
273  scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
274  EXPECT_CALL(*connection_,
275              SendConnectionCloseWithDetails(
276                  QUIC_INVALID_HEADERS_STREAM_DATA,
277                  "SPDY SETTINGS frame received."))
278      .WillOnce(InvokeWithoutArgs(this,
279                                  &QuicHeadersStreamTest::CloseConnection));
280  headers_stream_->ProcessRawData(frame->data(), frame->size());
281}
282
283TEST_P(QuicHeadersStreamTest, ProcessSpdyPingFrame) {
284  SpdyPingIR data(1);
285  scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
286  EXPECT_CALL(*connection_,
287              SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
288                                             "SPDY PING frame received."))
289      .WillOnce(InvokeWithoutArgs(this,
290                                  &QuicHeadersStreamTest::CloseConnection));
291  headers_stream_->ProcessRawData(frame->data(), frame->size());
292}
293
294TEST_P(QuicHeadersStreamTest, ProcessSpdyGoAwayFrame) {
295  SpdyGoAwayIR data(1, GOAWAY_PROTOCOL_ERROR, "go away");
296  scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
297  EXPECT_CALL(*connection_,
298              SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
299                                             "SPDY GOAWAY frame received."))
300      .WillOnce(InvokeWithoutArgs(this,
301                                  &QuicHeadersStreamTest::CloseConnection));
302  headers_stream_->ProcessRawData(frame->data(), frame->size());
303}
304
305TEST_P(QuicHeadersStreamTest, ProcessSpdyHeadersFrame) {
306  SpdyHeadersIR data(1);
307  scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
308  EXPECT_CALL(*connection_,
309              SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
310                                             "SPDY HEADERS frame received."))
311      .WillOnce(InvokeWithoutArgs(this,
312                                  &QuicHeadersStreamTest::CloseConnection));
313  headers_stream_->ProcessRawData(frame->data(), frame->size());
314}
315
316TEST_P(QuicHeadersStreamTest, ProcessSpdyWindowUpdateFrame) {
317  SpdyWindowUpdateIR data(1, 1);
318  scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
319  EXPECT_CALL(*connection_,
320              SendConnectionCloseWithDetails(
321                  QUIC_INVALID_HEADERS_STREAM_DATA,
322                  "SPDY WINDOW_UPDATE frame received."))
323      .WillOnce(InvokeWithoutArgs(this,
324                                  &QuicHeadersStreamTest::CloseConnection));
325  headers_stream_->ProcessRawData(frame->data(), frame->size());
326}
327
328TEST_P(QuicHeadersStreamTest, NoConnectionLevelFlowControl) {
329  if (connection_->version() < QUIC_VERSION_21) {
330    EXPECT_FALSE(headers_stream_->flow_controller()->IsEnabled());
331  } else {
332    EXPECT_TRUE(headers_stream_->flow_controller()->IsEnabled());
333  }
334  EXPECT_FALSE(ReliableQuicStreamPeer::StreamContributesToConnectionFlowControl(
335      headers_stream_));
336}
337
338}  // namespace
339}  // namespace test
340}  // namespace net
341