| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "net/quic/reliable_quic_stream.h" | 5 #include "net/quic/reliable_quic_stream.h" |
| 6 | 6 |
| 7 #include "net/quic/quic_connection.h" | 7 #include "net/quic/quic_connection.h" |
| 8 #include "net/quic/quic_spdy_compressor.h" |
| 9 #include "net/quic/quic_spdy_decompressor.h" |
| 8 #include "net/quic/quic_utils.h" | 10 #include "net/quic/quic_utils.h" |
| 11 #include "net/quic/spdy_utils.h" |
| 9 #include "net/quic/test_tools/quic_test_utils.h" | 12 #include "net/quic/test_tools/quic_test_utils.h" |
| 10 #include "testing/gmock/include/gmock/gmock.h" | 13 #include "testing/gmock/include/gmock/gmock.h" |
| 11 | 14 |
| 12 using base::StringPiece; | 15 using base::StringPiece; |
| 16 using std::min; |
| 13 using testing::_; | 17 using testing::_; |
| 14 using testing::InSequence; | 18 using testing::InSequence; |
| 15 using testing::Return; | 19 using testing::Return; |
| 20 using testing::SaveArg; |
| 16 using testing::StrEq; | 21 using testing::StrEq; |
| 22 using testing::StrictMock; |
| 17 | 23 |
| 18 namespace net { | 24 namespace net { |
| 19 namespace test { | 25 namespace test { |
| 20 namespace { | 26 namespace { |
| 21 | 27 |
| 22 const char kData1[] = "FooAndBar"; | 28 const char kData1[] = "FooAndBar"; |
| 23 const char kData2[] = "EepAndBaz"; | 29 const char kData2[] = "EepAndBaz"; |
| 24 const size_t kDataLen = 9; | 30 const size_t kDataLen = 9; |
| 25 | 31 const QuicGuid kGuid = 42; |
| 26 class QuicReliableTestStream : public ReliableQuicStream { | 32 const QuicGuid kStreamId = 3; |
| 33 const bool kIsServer = true; |
| 34 const bool kShouldProcessData = true; |
| 35 |
| 36 class TestStream : public ReliableQuicStream { |
| 27 public: | 37 public: |
| 28 QuicReliableTestStream(QuicStreamId id, QuicSession* session) | 38 TestStream(QuicStreamId id, |
| 29 : ReliableQuicStream(id, session) { | 39 QuicSession* session, |
| 30 } | 40 bool should_process_data) |
| 41 : ReliableQuicStream(id, session), |
| 42 should_process_data_(should_process_data) { |
| 43 } |
| 44 |
| 31 virtual uint32 ProcessData(const char* data, uint32 data_len) OVERRIDE { | 45 virtual uint32 ProcessData(const char* data, uint32 data_len) OVERRIDE { |
| 32 return 0; | 46 LOG(INFO) << "data_len: " << data_len; |
| 33 } | 47 data_ += string(data, data_len); |
| 48 return should_process_data_ ? data_len : 0; |
| 49 } |
| 50 |
| 34 using ReliableQuicStream::WriteData; | 51 using ReliableQuicStream::WriteData; |
| 35 using ReliableQuicStream::CloseReadSide; | 52 using ReliableQuicStream::CloseReadSide; |
| 36 using ReliableQuicStream::CloseWriteSide; | 53 using ReliableQuicStream::CloseWriteSide; |
| 54 |
| 55 const string& data() const { return data_; } |
| 56 |
| 57 private: |
| 58 bool should_process_data_; |
| 59 string data_; |
| 37 }; | 60 }; |
| 38 | 61 |
| 39 class ReliableQuicStreamTest : public ::testing::TestWithParam<bool> { | 62 class ReliableQuicStreamTest : public ::testing::TestWithParam<bool> { |
| 40 public: | 63 public: |
| 41 ReliableQuicStreamTest() | 64 ReliableQuicStreamTest() { |
| 42 : connection_(new MockConnection(1, IPEndPoint(), false)), | 65 headers_[":host"] = "www.google.com"; |
| 43 session_(connection_, true), | 66 headers_[":path"] = "/index.hml"; |
| 44 stream_(1, &session_) { | 67 headers_[":scheme"] = "https"; |
| 45 } | 68 } |
| 46 | 69 |
| 70 void Initialize(bool stream_should_process_data) { |
| 71 connection_ = new MockConnection(kGuid, IPEndPoint(), kIsServer); |
| 72 session_.reset(new MockSession(connection_, kIsServer)); |
| 73 stream_.reset(new TestStream(kStreamId, session_.get(), |
| 74 stream_should_process_data)); |
| 75 stream2_.reset(new TestStream(kStreamId + 2, session_.get(), |
| 76 stream_should_process_data)); |
| 77 compressor_.reset(new QuicSpdyCompressor()); |
| 78 decompressor_.reset(new QuicSpdyDecompressor); |
| 79 } |
| 80 |
| 81 protected: |
| 47 MockConnection* connection_; | 82 MockConnection* connection_; |
| 48 MockSession session_; | 83 scoped_ptr<MockSession> session_; |
| 49 QuicReliableTestStream stream_; | 84 scoped_ptr<TestStream> stream_; |
| 85 scoped_ptr<TestStream> stream2_; |
| 86 scoped_ptr<QuicSpdyCompressor> compressor_; |
| 87 scoped_ptr<QuicSpdyDecompressor> decompressor_; |
| 88 SpdyHeaderBlock headers_; |
| 50 }; | 89 }; |
| 51 | 90 |
| 52 TEST_F(ReliableQuicStreamTest, WriteAllData) { | 91 TEST_F(ReliableQuicStreamTest, WriteAllData) { |
| 92 Initialize(kShouldProcessData); |
| 93 |
| 53 connection_->options()->max_packet_length = | 94 connection_->options()->max_packet_length = |
| 54 1 + QuicPacketCreator::StreamFramePacketOverhead(1, !kIncludeVersion); | 95 1 + QuicPacketCreator::StreamFramePacketOverhead(1, !kIncludeVersion); |
| 55 // TODO(rch): figure out how to get StrEq working here. | 96 // TODO(rch): figure out how to get StrEq working here. |
| 56 //EXPECT_CALL(session_, WriteData(_, StrEq(kData1), _, _)).WillOnce( | 97 //EXPECT_CALL(*session_, WriteData(kStreamId, StrEq(kData1), _, _)).WillOnce( |
| 57 EXPECT_CALL(session_, WriteData(1, _, _, _)).WillOnce( | 98 EXPECT_CALL(*session_, WriteData(kStreamId, _, _, _)).WillOnce( |
| 58 Return(QuicConsumedData(kDataLen, true))); | 99 Return(QuicConsumedData(kDataLen, true))); |
| 59 EXPECT_EQ(kDataLen, stream_.WriteData(kData1, false).bytes_consumed); | 100 EXPECT_EQ(kDataLen, stream_->WriteData(kData1, false).bytes_consumed); |
| 60 } | 101 } |
| 61 | 102 |
| 62 TEST_F(ReliableQuicStreamTest, WriteData) { | 103 TEST_F(ReliableQuicStreamTest, WriteData) { |
| 104 Initialize(kShouldProcessData); |
| 105 |
| 63 connection_->options()->max_packet_length = | 106 connection_->options()->max_packet_length = |
| 64 1 + QuicPacketCreator::StreamFramePacketOverhead(1, !kIncludeVersion); | 107 1 + QuicPacketCreator::StreamFramePacketOverhead(1, !kIncludeVersion); |
| 65 // TODO(rch): figure out how to get StrEq working here. | 108 // TODO(rch): figure out how to get StrEq working here. |
| 66 //EXPECT_CALL(session_, WriteData(_, StrEq(kData1), _, _)).WillOnce( | 109 //EXPECT_CALL(*session_, WriteData(_, StrEq(kData1), _, _)).WillOnce( |
| 67 EXPECT_CALL(session_, WriteData(_, _, _, _)).WillOnce( | 110 EXPECT_CALL(*session_, WriteData(_, _, _, _)).WillOnce( |
| 68 Return(QuicConsumedData(kDataLen - 1, false))); | 111 Return(QuicConsumedData(kDataLen - 1, false))); |
| 69 // The return will be kDataLen, because the last byte gets buffered. | 112 // The return will be kDataLen, because the last byte gets buffered. |
| 70 EXPECT_EQ(kDataLen, stream_.WriteData(kData1, false).bytes_consumed); | 113 EXPECT_EQ(kDataLen, stream_->WriteData(kData1, false).bytes_consumed); |
| 71 | 114 |
| 72 // Queue a bytes_consumed write. | 115 // Queue a bytes_consumed write. |
| 73 EXPECT_EQ(kDataLen, stream_.WriteData(kData2, false).bytes_consumed); | 116 EXPECT_EQ(kDataLen, stream_->WriteData(kData2, false).bytes_consumed); |
| 74 | 117 |
| 75 // Make sure we get the tail of the first write followed by the bytes_consumed | 118 // Make sure we get the tail of the first write followed by the bytes_consumed |
| 76 InSequence s; | 119 InSequence s; |
| 77 //EXPECT_CALL(session_, WriteData(_, StrEq(&kData2[kDataLen - 1]), _, _)). | 120 //EXPECT_CALL(*session_, WriteData(_, StrEq(&kData1[kDataLen - 1]), _, _)). |
| 78 EXPECT_CALL(session_, WriteData(_, _, _, _)). | 121 EXPECT_CALL(*session_, WriteData(_, _, _, _)). |
| 79 WillOnce(Return(QuicConsumedData(1, false))); | 122 WillOnce(Return(QuicConsumedData(1, false))); |
| 80 //EXPECT_CALL(session_, WriteData(_, StrEq(kData2), _, _)). | 123 //EXPECT_CALL(*session_, WriteData(_, StrEq(kData2), _, _)). |
| 81 EXPECT_CALL(session_, WriteData(_, _, _, _)). | 124 EXPECT_CALL(*session_, WriteData(_, _, _, _)). |
| 82 WillOnce(Return(QuicConsumedData(kDataLen - 2, false))); | 125 WillOnce(Return(QuicConsumedData(kDataLen - 2, false))); |
| 83 stream_.OnCanWrite(); | 126 stream_->OnCanWrite(); |
| 84 | 127 |
| 85 // And finally the end of the bytes_consumed | 128 // And finally the end of the bytes_consumed |
| 86 //EXPECT_CALL(session_, WriteData(_, StrEq(&kData2[kDataLen - 2]), _, _)). | 129 //EXPECT_CALL(*session_, WriteData(_, StrEq(&kData2[kDataLen - 2]), _, _)). |
| 87 EXPECT_CALL(session_, WriteData(_, _, _, _)). | 130 EXPECT_CALL(*session_, WriteData(_, _, _, _)). |
| 88 WillOnce(Return(QuicConsumedData(2, true))); | 131 WillOnce(Return(QuicConsumedData(2, true))); |
| 89 stream_.OnCanWrite(); | 132 stream_->OnCanWrite(); |
| 90 } | 133 } |
| 91 | 134 |
| 92 TEST_F(ReliableQuicStreamTest, ConnectionCloseAfterStreamClose) { | 135 TEST_F(ReliableQuicStreamTest, ConnectionCloseAfterStreamClose) { |
| 93 stream_.CloseReadSide(); | 136 Initialize(kShouldProcessData); |
| 94 stream_.CloseWriteSide(); | 137 |
| 95 EXPECT_EQ(QUIC_STREAM_NO_ERROR, stream_.stream_error()); | 138 stream_->CloseReadSide(); |
| 96 EXPECT_EQ(QUIC_NO_ERROR, stream_.connection_error()); | 139 stream_->CloseWriteSide(); |
| 97 stream_.ConnectionClose(QUIC_INTERNAL_ERROR, false); | 140 EXPECT_EQ(QUIC_STREAM_NO_ERROR, stream_->stream_error()); |
| 98 EXPECT_EQ(QUIC_STREAM_NO_ERROR, stream_.stream_error()); | 141 EXPECT_EQ(QUIC_NO_ERROR, stream_->connection_error()); |
| 99 EXPECT_EQ(QUIC_NO_ERROR, stream_.connection_error()); | 142 stream_->ConnectionClose(QUIC_INTERNAL_ERROR, false); |
| 143 EXPECT_EQ(QUIC_STREAM_NO_ERROR, stream_->stream_error()); |
| 144 EXPECT_EQ(QUIC_NO_ERROR, stream_->connection_error()); |
| 145 } |
| 146 |
| 147 TEST_F(ReliableQuicStreamTest, ProcessHeaders) { |
| 148 Initialize(kShouldProcessData); |
| 149 |
| 150 string compressed_headers = compressor_->CompressHeaders(headers_); |
| 151 QuicStreamFrame frame(kStreamId, false, 0, compressed_headers); |
| 152 |
| 153 stream_->OnStreamFrame(frame); |
| 154 EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers_), stream_->data()); |
| 155 } |
| 156 |
| 157 TEST_F(ReliableQuicStreamTest, ProcessHeadersWithInvalidHeaderId) { |
| 158 Initialize(kShouldProcessData); |
| 159 |
| 160 string compressed_headers = compressor_->CompressHeaders(headers_); |
| 161 compressed_headers.replace(0, 1, 1, '\xFF'); // Illegal header id. |
| 162 QuicStreamFrame frame(kStreamId, false, 0, compressed_headers); |
| 163 |
| 164 EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_HEADER_ID)); |
| 165 stream_->OnStreamFrame(frame); |
| 166 } |
| 167 |
| 168 TEST_F(ReliableQuicStreamTest, ProcessHeadersAndBody) { |
| 169 Initialize(kShouldProcessData); |
| 170 |
| 171 string compressed_headers = compressor_->CompressHeaders(headers_); |
| 172 string body = "this is the body"; |
| 173 string data = compressed_headers + body; |
| 174 QuicStreamFrame frame(kStreamId, false, 0, data); |
| 175 |
| 176 stream_->OnStreamFrame(frame); |
| 177 EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers_) + body, |
| 178 stream_->data()); |
| 179 } |
| 180 |
| 181 TEST_F(ReliableQuicStreamTest, ProcessHeadersAndBodyFragments) { |
| 182 Initialize(kShouldProcessData); |
| 183 |
| 184 string compressed_headers = compressor_->CompressHeaders(headers_); |
| 185 string body = "this is the body"; |
| 186 string data = compressed_headers + body; |
| 187 |
| 188 for (size_t fragment_size = 1; fragment_size < data.size(); ++fragment_size) { |
| 189 Initialize(kShouldProcessData); |
| 190 for (size_t offset = 0; offset < data.size(); offset += fragment_size) { |
| 191 size_t remaining_data = data.length() - offset; |
| 192 StringPiece fragment(data.data() + offset, |
| 193 min(fragment_size, remaining_data)); |
| 194 QuicStreamFrame frame(kStreamId, false, offset, fragment); |
| 195 |
| 196 stream_->OnStreamFrame(frame); |
| 197 } |
| 198 ASSERT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers_) + body, |
| 199 stream_->data()) << "fragment_size: " << fragment_size; |
| 200 } |
| 201 } |
| 202 |
| 203 TEST_F(ReliableQuicStreamTest, ProcessHeadersAndBodyReadv) { |
| 204 Initialize(!kShouldProcessData); |
| 205 |
| 206 string compressed_headers = compressor_->CompressHeaders(headers_); |
| 207 string body = "this is the body"; |
| 208 string data = compressed_headers + body; |
| 209 QuicStreamFrame frame(kStreamId, false, 0, data); |
| 210 string uncompressed_headers = |
| 211 SpdyUtils::SerializeUncompressedHeaders(headers_); |
| 212 string uncompressed_data = uncompressed_headers + body; |
| 213 |
| 214 stream_->OnStreamFrame(frame); |
| 215 EXPECT_EQ(uncompressed_data, stream_->data()); |
| 216 |
| 217 char buffer[1024]; |
| 218 ASSERT_LT(data.length(), arraysize(buffer)); |
| 219 struct iovec vec; |
| 220 vec.iov_base = buffer; |
| 221 vec.iov_len = arraysize(buffer); |
| 222 |
| 223 size_t bytes_read = stream_->Readv(&vec, 1); |
| 224 EXPECT_EQ(uncompressed_headers.length(), bytes_read); |
| 225 EXPECT_EQ(uncompressed_headers, string(buffer, bytes_read)); |
| 226 |
| 227 bytes_read = stream_->Readv(&vec, 1); |
| 228 EXPECT_EQ(body.length(), bytes_read); |
| 229 EXPECT_EQ(body, string(buffer, bytes_read)); |
| 230 } |
| 231 |
| 232 TEST_F(ReliableQuicStreamTest, ProcessHeadersAndBodyIncrementalReadv) { |
| 233 Initialize(!kShouldProcessData); |
| 234 |
| 235 string compressed_headers = compressor_->CompressHeaders(headers_); |
| 236 string body = "this is the body"; |
| 237 string data = compressed_headers + body; |
| 238 QuicStreamFrame frame(kStreamId, false, 0, data); |
| 239 string uncompressed_headers = |
| 240 SpdyUtils::SerializeUncompressedHeaders(headers_); |
| 241 string uncompressed_data = uncompressed_headers + body; |
| 242 |
| 243 stream_->OnStreamFrame(frame); |
| 244 EXPECT_EQ(uncompressed_data, stream_->data()); |
| 245 |
| 246 char buffer[1]; |
| 247 struct iovec vec; |
| 248 vec.iov_base = buffer; |
| 249 vec.iov_len = arraysize(buffer); |
| 250 for (size_t i = 0; i < uncompressed_data.length(); ++i) { |
| 251 size_t bytes_read = stream_->Readv(&vec, 1); |
| 252 ASSERT_EQ(1u, bytes_read); |
| 253 EXPECT_EQ(uncompressed_data.data()[i], buffer[0]); |
| 254 } |
| 255 } |
| 256 |
| 257 TEST_F(ReliableQuicStreamTest, ProcessHeadersUsingReadvWithMultipleIovecs) { |
| 258 Initialize(!kShouldProcessData); |
| 259 |
| 260 string compressed_headers = compressor_->CompressHeaders(headers_); |
| 261 string body = "this is the body"; |
| 262 string data = compressed_headers + body; |
| 263 QuicStreamFrame frame(kStreamId, false, 0, data); |
| 264 string uncompressed_headers = |
| 265 SpdyUtils::SerializeUncompressedHeaders(headers_); |
| 266 string uncompressed_data = uncompressed_headers + body; |
| 267 |
| 268 stream_->OnStreamFrame(frame); |
| 269 EXPECT_EQ(uncompressed_data, stream_->data()); |
| 270 |
| 271 char buffer1[1]; |
| 272 char buffer2[1]; |
| 273 struct iovec vec[2]; |
| 274 vec[0].iov_base = buffer1; |
| 275 vec[0].iov_len = arraysize(buffer1); |
| 276 vec[1].iov_base = buffer2; |
| 277 vec[1].iov_len = arraysize(buffer2); |
| 278 for (size_t i = 0; i < uncompressed_data.length(); i += 2) { |
| 279 size_t bytes_read = stream_->Readv(vec, 2); |
| 280 ASSERT_EQ(2u, bytes_read) << i; |
| 281 ASSERT_EQ(uncompressed_data.data()[i], buffer1[0]) << i; |
| 282 ASSERT_EQ(uncompressed_data.data()[i + 1], buffer2[0]) << i; |
| 283 } |
| 284 } |
| 285 |
| 286 TEST_F(ReliableQuicStreamTest, ProcessHeadersEarly) { |
| 287 Initialize(kShouldProcessData); |
| 288 |
| 289 string compressed_headers1 = compressor_->CompressHeaders(headers_); |
| 290 QuicStreamFrame frame1(stream_->id(), false, 0, compressed_headers1); |
| 291 string decompressed_headers1 = |
| 292 SpdyUtils::SerializeUncompressedHeaders(headers_); |
| 293 |
| 294 headers_["content-type"] = "text/plain"; |
| 295 string compressed_headers2 = compressor_->CompressHeaders(headers_); |
| 296 QuicStreamFrame frame2(stream2_->id(), false, 0, compressed_headers2); |
| 297 string decompressed_headers2 = |
| 298 SpdyUtils::SerializeUncompressedHeaders(headers_); |
| 299 |
| 300 stream2_->OnStreamFrame(frame2); |
| 301 EXPECT_EQ("", stream_->data()); |
| 302 |
| 303 stream_->OnStreamFrame(frame1); |
| 304 EXPECT_EQ(decompressed_headers1, stream_->data()); |
| 305 |
| 306 EXPECT_EQ(2u, session_->decompressor()->current_header_id()); |
| 307 stream2_->OnDecompressorAvailable(); |
| 308 EXPECT_EQ(decompressed_headers2, stream2_->data()); |
| 100 } | 309 } |
| 101 | 310 |
| 102 } // namespace | 311 } // namespace |
| 103 } // namespace test | 312 } // namespace test |
| 104 } // namespace net | 313 } // namespace net |
| OLD | NEW |