| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 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/quic_data_stream.h" | 5 #include "net/quic/quic_spdy_stream.h" |
| 6 | 6 |
| 7 #include "net/quic/quic_ack_notifier.h" | 7 #include "net/quic/quic_ack_notifier.h" |
| 8 #include "net/quic/quic_connection.h" | 8 #include "net/quic/quic_connection.h" |
| 9 #include "net/quic/quic_utils.h" | 9 #include "net/quic/quic_utils.h" |
| 10 #include "net/quic/quic_write_blocked_list.h" | 10 #include "net/quic/quic_write_blocked_list.h" |
| 11 #include "net/quic/spdy_utils.h" | 11 #include "net/quic/spdy_utils.h" |
| 12 #include "net/quic/test_tools/quic_flow_controller_peer.h" | 12 #include "net/quic/test_tools/quic_flow_controller_peer.h" |
| 13 #include "net/quic/test_tools/quic_session_peer.h" | 13 #include "net/quic/test_tools/quic_session_peer.h" |
| 14 #include "net/quic/test_tools/quic_test_utils.h" | 14 #include "net/quic/test_tools/quic_test_utils.h" |
| 15 #include "net/quic/test_tools/reliable_quic_stream_peer.h" | 15 #include "net/quic/test_tools/reliable_quic_stream_peer.h" |
| 16 #include "net/test/gtest_util.h" | 16 #include "net/test/gtest_util.h" |
| 17 #include "testing/gmock/include/gmock/gmock.h" | 17 #include "testing/gmock/include/gmock/gmock.h" |
| 18 | 18 |
| 19 using base::StringPiece; | 19 using base::StringPiece; |
| 20 using std::min; | 20 using std::min; |
| 21 using std::string; | 21 using std::string; |
| 22 using testing::Return; | 22 using testing::Return; |
| 23 using testing::StrictMock; | 23 using testing::StrictMock; |
| 24 using testing::_; | 24 using testing::_; |
| 25 | 25 |
| 26 namespace net { | 26 namespace net { |
| 27 namespace test { | 27 namespace test { |
| 28 namespace { | 28 namespace { |
| 29 | 29 |
| 30 const bool kShouldProcessData = true; | 30 const bool kShouldProcessData = true; |
| 31 | 31 |
| 32 class TestStream : public QuicDataStream { | 32 class TestStream : public QuicSpdyStream { |
| 33 public: | 33 public: |
| 34 TestStream(QuicStreamId id, | 34 TestStream(QuicStreamId id, |
| 35 QuicSpdySession* session, | 35 QuicSpdySession* session, |
| 36 bool should_process_data) | 36 bool should_process_data) |
| 37 : QuicDataStream(id, session), | 37 : QuicSpdyStream(id, session), |
| 38 should_process_data_(should_process_data) {} | 38 should_process_data_(should_process_data) {} |
| 39 | 39 |
| 40 void OnDataAvailable() override { | 40 void OnDataAvailable() override { |
| 41 if (!should_process_data_) { | 41 if (!should_process_data_) { |
| 42 return; | 42 return; |
| 43 } | 43 } |
| 44 char buffer[2048]; | 44 char buffer[2048]; |
| 45 struct iovec vec; | 45 struct iovec vec; |
| 46 vec.iov_base = buffer; | 46 vec.iov_base = buffer; |
| 47 vec.iov_len = arraysize(buffer); | 47 vec.iov_len = arraysize(buffer); |
| 48 size_t bytes_read = Readv(&vec, 1); | 48 size_t bytes_read = Readv(&vec, 1); |
| 49 data_ += string(buffer, bytes_read); | 49 data_ += string(buffer, bytes_read); |
| 50 } | 50 } |
| 51 | 51 |
| 52 using ReliableQuicStream::WriteOrBufferData; | 52 using ReliableQuicStream::WriteOrBufferData; |
| 53 using ReliableQuicStream::CloseWriteSide; | 53 using ReliableQuicStream::CloseWriteSide; |
| 54 | 54 |
| 55 const string& data() const { return data_; } | 55 const string& data() const { return data_; } |
| 56 | 56 |
| 57 private: | 57 private: |
| 58 bool should_process_data_; | 58 bool should_process_data_; |
| 59 string data_; | 59 string data_; |
| 60 }; | 60 }; |
| 61 | 61 |
| 62 class QuicDataStreamTest : public ::testing::Test { | 62 class QuicSpdyStreamTest : public ::testing::Test { |
| 63 public: | 63 public: |
| 64 QuicDataStreamTest() { | 64 QuicSpdyStreamTest() { |
| 65 headers_[":host"] = "www.google.com"; | 65 headers_[":host"] = "www.google.com"; |
| 66 headers_[":path"] = "/index.hml"; | 66 headers_[":path"] = "/index.hml"; |
| 67 headers_[":scheme"] = "https"; | 67 headers_[":scheme"] = "https"; |
| 68 headers_["cookie"] = | 68 headers_["cookie"] = |
| 69 "__utma=208381060.1228362404.1372200928.1372200928.1372200928.1; " | 69 "__utma=208381060.1228362404.1372200928.1372200928.1372200928.1; " |
| 70 "__utmc=160408618; " | 70 "__utmc=160408618; " |
| 71 "GX=DQAAAOEAAACWJYdewdE9rIrW6qw3PtVi2-d729qaa-74KqOsM1NVQblK4VhX" | 71 "GX=DQAAAOEAAACWJYdewdE9rIrW6qw3PtVi2-d729qaa-74KqOsM1NVQblK4VhX" |
| 72 "hoALMsy6HOdDad2Sz0flUByv7etmo3mLMidGrBoljqO9hSVA40SLqpG_iuKKSHX" | 72 "hoALMsy6HOdDad2Sz0flUByv7etmo3mLMidGrBoljqO9hSVA40SLqpG_iuKKSHX" |
| 73 "RW3Np4bq0F0SDGDNsW0DSmTS9ufMRrlpARJDS7qAI6M3bghqJp4eABKZiRqebHT" | 73 "RW3Np4bq0F0SDGDNsW0DSmTS9ufMRrlpARJDS7qAI6M3bghqJp4eABKZiRqebHT" |
| 74 "pMU-RXvTI5D5oCF1vYxYofH_l1Kviuiy3oQ1kS1enqWgbhJ2t61_SNdv-1XJIS0" | 74 "pMU-RXvTI5D5oCF1vYxYofH_l1Kviuiy3oQ1kS1enqWgbhJ2t61_SNdv-1XJIS0" |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 106 protected: | 106 protected: |
| 107 MockHelper helper_; | 107 MockHelper helper_; |
| 108 MockConnection* connection_; | 108 MockConnection* connection_; |
| 109 scoped_ptr<MockQuicSpdySession> session_; | 109 scoped_ptr<MockQuicSpdySession> session_; |
| 110 scoped_ptr<TestStream> stream_; | 110 scoped_ptr<TestStream> stream_; |
| 111 scoped_ptr<TestStream> stream2_; | 111 scoped_ptr<TestStream> stream2_; |
| 112 SpdyHeaderBlock headers_; | 112 SpdyHeaderBlock headers_; |
| 113 QuicWriteBlockedList* write_blocked_list_; | 113 QuicWriteBlockedList* write_blocked_list_; |
| 114 }; | 114 }; |
| 115 | 115 |
| 116 TEST_F(QuicDataStreamTest, ProcessHeaders) { | 116 TEST_F(QuicSpdyStreamTest, ProcessHeaders) { |
| 117 Initialize(kShouldProcessData); | 117 Initialize(kShouldProcessData); |
| 118 | 118 |
| 119 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); | 119 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); |
| 120 stream_->OnStreamHeadersPriority(QuicUtils::HighestPriority()); | 120 stream_->OnStreamHeadersPriority(QuicUtils::HighestPriority()); |
| 121 stream_->OnStreamHeaders(headers); | 121 stream_->OnStreamHeaders(headers); |
| 122 EXPECT_EQ("", stream_->data()); | 122 EXPECT_EQ("", stream_->data()); |
| 123 EXPECT_EQ(headers, stream_->decompressed_headers()); | 123 EXPECT_EQ(headers, stream_->decompressed_headers()); |
| 124 stream_->OnStreamHeadersComplete(false, headers.size()); | 124 stream_->OnStreamHeadersComplete(false, headers.size()); |
| 125 EXPECT_EQ(QuicUtils::HighestPriority(), stream_->EffectivePriority()); | 125 EXPECT_EQ(QuicUtils::HighestPriority(), stream_->EffectivePriority()); |
| 126 EXPECT_EQ("", stream_->data()); | 126 EXPECT_EQ("", stream_->data()); |
| 127 EXPECT_EQ(headers, stream_->decompressed_headers()); | 127 EXPECT_EQ(headers, stream_->decompressed_headers()); |
| 128 EXPECT_FALSE(stream_->IsDoneReading()); | 128 EXPECT_FALSE(stream_->IsDoneReading()); |
| 129 } | 129 } |
| 130 | 130 |
| 131 TEST_F(QuicDataStreamTest, ProcessHeadersWithFin) { | 131 TEST_F(QuicSpdyStreamTest, ProcessHeadersWithFin) { |
| 132 Initialize(kShouldProcessData); | 132 Initialize(kShouldProcessData); |
| 133 | 133 |
| 134 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); | 134 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); |
| 135 stream_->OnStreamHeadersPriority(QuicUtils::HighestPriority()); | 135 stream_->OnStreamHeadersPriority(QuicUtils::HighestPriority()); |
| 136 stream_->OnStreamHeaders(headers); | 136 stream_->OnStreamHeaders(headers); |
| 137 EXPECT_EQ("", stream_->data()); | 137 EXPECT_EQ("", stream_->data()); |
| 138 EXPECT_EQ(headers, stream_->decompressed_headers()); | 138 EXPECT_EQ(headers, stream_->decompressed_headers()); |
| 139 stream_->OnStreamHeadersComplete(true, headers.size()); | 139 stream_->OnStreamHeadersComplete(true, headers.size()); |
| 140 EXPECT_EQ(QuicUtils::HighestPriority(), stream_->EffectivePriority()); | 140 EXPECT_EQ(QuicUtils::HighestPriority(), stream_->EffectivePriority()); |
| 141 EXPECT_EQ("", stream_->data()); | 141 EXPECT_EQ("", stream_->data()); |
| 142 EXPECT_EQ(headers, stream_->decompressed_headers()); | 142 EXPECT_EQ(headers, stream_->decompressed_headers()); |
| 143 EXPECT_FALSE(stream_->IsDoneReading()); | 143 EXPECT_FALSE(stream_->IsDoneReading()); |
| 144 EXPECT_TRUE(stream_->HasFinalReceivedByteOffset()); | 144 EXPECT_TRUE(stream_->HasFinalReceivedByteOffset()); |
| 145 } | 145 } |
| 146 | 146 |
| 147 TEST_F(QuicDataStreamTest, MarkHeadersConsumed) { | 147 TEST_F(QuicSpdyStreamTest, MarkHeadersConsumed) { |
| 148 Initialize(kShouldProcessData); | 148 Initialize(kShouldProcessData); |
| 149 | 149 |
| 150 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); | 150 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); |
| 151 string body = "this is the body"; | 151 string body = "this is the body"; |
| 152 | 152 |
| 153 stream_->OnStreamHeaders(headers); | 153 stream_->OnStreamHeaders(headers); |
| 154 stream_->OnStreamHeadersComplete(false, headers.size()); | 154 stream_->OnStreamHeadersComplete(false, headers.size()); |
| 155 EXPECT_EQ(headers, stream_->decompressed_headers()); | 155 EXPECT_EQ(headers, stream_->decompressed_headers()); |
| 156 | 156 |
| 157 headers.erase(0, 10); | 157 headers.erase(0, 10); |
| 158 stream_->MarkHeadersConsumed(10); | 158 stream_->MarkHeadersConsumed(10); |
| 159 EXPECT_EQ(headers, stream_->decompressed_headers()); | 159 EXPECT_EQ(headers, stream_->decompressed_headers()); |
| 160 | 160 |
| 161 stream_->MarkHeadersConsumed(headers.length()); | 161 stream_->MarkHeadersConsumed(headers.length()); |
| 162 EXPECT_EQ("", stream_->decompressed_headers()); | 162 EXPECT_EQ("", stream_->decompressed_headers()); |
| 163 } | 163 } |
| 164 | 164 |
| 165 TEST_F(QuicDataStreamTest, ProcessHeadersAndBody) { | 165 TEST_F(QuicSpdyStreamTest, ProcessHeadersAndBody) { |
| 166 Initialize(kShouldProcessData); | 166 Initialize(kShouldProcessData); |
| 167 | 167 |
| 168 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); | 168 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); |
| 169 string body = "this is the body"; | 169 string body = "this is the body"; |
| 170 | 170 |
| 171 stream_->OnStreamHeaders(headers); | 171 stream_->OnStreamHeaders(headers); |
| 172 EXPECT_EQ("", stream_->data()); | 172 EXPECT_EQ("", stream_->data()); |
| 173 EXPECT_EQ(headers, stream_->decompressed_headers()); | 173 EXPECT_EQ(headers, stream_->decompressed_headers()); |
| 174 stream_->OnStreamHeadersComplete(false, headers.size()); | 174 stream_->OnStreamHeadersComplete(false, headers.size()); |
| 175 EXPECT_EQ(headers, stream_->decompressed_headers()); | 175 EXPECT_EQ(headers, stream_->decompressed_headers()); |
| 176 stream_->MarkHeadersConsumed(headers.length()); | 176 stream_->MarkHeadersConsumed(headers.length()); |
| 177 QuicStreamFrame frame(kClientDataStreamId1, false, 0, StringPiece(body)); | 177 QuicStreamFrame frame(kClientDataStreamId1, false, 0, StringPiece(body)); |
| 178 stream_->OnStreamFrame(frame); | 178 stream_->OnStreamFrame(frame); |
| 179 EXPECT_EQ("", stream_->decompressed_headers()); | 179 EXPECT_EQ("", stream_->decompressed_headers()); |
| 180 EXPECT_EQ(body, stream_->data()); | 180 EXPECT_EQ(body, stream_->data()); |
| 181 } | 181 } |
| 182 | 182 |
| 183 TEST_F(QuicDataStreamTest, ProcessHeadersAndBodyFragments) { | 183 TEST_F(QuicSpdyStreamTest, ProcessHeadersAndBodyFragments) { |
| 184 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); | 184 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); |
| 185 string body = "this is the body"; | 185 string body = "this is the body"; |
| 186 | 186 |
| 187 for (size_t fragment_size = 1; fragment_size < body.size(); | 187 for (size_t fragment_size = 1; fragment_size < body.size(); ++fragment_size) { |
| 188 ++fragment_size) { | |
| 189 Initialize(kShouldProcessData); | 188 Initialize(kShouldProcessData); |
| 190 for (size_t offset = 0; offset < headers.size(); | 189 for (size_t offset = 0; offset < headers.size(); offset += fragment_size) { |
| 191 offset += fragment_size) { | |
| 192 size_t remaining_data = headers.size() - offset; | 190 size_t remaining_data = headers.size() - offset; |
| 193 StringPiece fragment(headers.data() + offset, | 191 StringPiece fragment(headers.data() + offset, |
| 194 min(fragment_size, remaining_data)); | 192 min(fragment_size, remaining_data)); |
| 195 stream_->OnStreamHeaders(fragment); | 193 stream_->OnStreamHeaders(fragment); |
| 196 } | 194 } |
| 197 stream_->OnStreamHeadersComplete(false, headers.size()); | 195 stream_->OnStreamHeadersComplete(false, headers.size()); |
| 198 ASSERT_EQ(headers, stream_->decompressed_headers()) | 196 ASSERT_EQ(headers, stream_->decompressed_headers()) << "fragment_size: " |
| 199 << "fragment_size: " << fragment_size; | 197 << fragment_size; |
| 200 stream_->MarkHeadersConsumed(headers.length()); | 198 stream_->MarkHeadersConsumed(headers.length()); |
| 201 for (size_t offset = 0; offset < body.size(); offset += fragment_size) { | 199 for (size_t offset = 0; offset < body.size(); offset += fragment_size) { |
| 202 size_t remaining_data = body.size() - offset; | 200 size_t remaining_data = body.size() - offset; |
| 203 StringPiece fragment(body.data() + offset, | 201 StringPiece fragment(body.data() + offset, |
| 204 min(fragment_size, remaining_data)); | 202 min(fragment_size, remaining_data)); |
| 205 QuicStreamFrame frame(kClientDataStreamId1, false, offset, | 203 QuicStreamFrame frame(kClientDataStreamId1, false, offset, |
| 206 StringPiece(fragment)); | 204 StringPiece(fragment)); |
| 207 stream_->OnStreamFrame(frame); | 205 stream_->OnStreamFrame(frame); |
| 208 } | 206 } |
| 209 ASSERT_EQ(body, stream_->data()) << "fragment_size: " << fragment_size; | 207 ASSERT_EQ(body, stream_->data()) << "fragment_size: " << fragment_size; |
| 210 } | 208 } |
| 211 } | 209 } |
| 212 | 210 |
| 213 TEST_F(QuicDataStreamTest, ProcessHeadersAndBodyFragmentsSplit) { | 211 TEST_F(QuicSpdyStreamTest, ProcessHeadersAndBodyFragmentsSplit) { |
| 214 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); | 212 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); |
| 215 string body = "this is the body"; | 213 string body = "this is the body"; |
| 216 | 214 |
| 217 for (size_t split_point = 1; split_point < body.size() - 1; ++split_point) { | 215 for (size_t split_point = 1; split_point < body.size() - 1; ++split_point) { |
| 218 Initialize(kShouldProcessData); | 216 Initialize(kShouldProcessData); |
| 219 StringPiece headers1(headers.data(), split_point); | 217 StringPiece headers1(headers.data(), split_point); |
| 220 stream_->OnStreamHeaders(headers1); | 218 stream_->OnStreamHeaders(headers1); |
| 221 | 219 |
| 222 StringPiece headers2(headers.data() + split_point, | 220 StringPiece headers2(headers.data() + split_point, |
| 223 headers.size() - split_point); | 221 headers.size() - split_point); |
| 224 stream_->OnStreamHeaders(headers2); | 222 stream_->OnStreamHeaders(headers2); |
| 225 stream_->OnStreamHeadersComplete(false, headers.size()); | 223 stream_->OnStreamHeadersComplete(false, headers.size()); |
| 226 ASSERT_EQ(headers, stream_->decompressed_headers()) | 224 ASSERT_EQ(headers, stream_->decompressed_headers()) << "split_point: " |
| 227 << "split_point: " << split_point; | 225 << split_point; |
| 228 stream_->MarkHeadersConsumed(headers.length()); | 226 stream_->MarkHeadersConsumed(headers.length()); |
| 229 | 227 |
| 230 StringPiece fragment1(body.data(), split_point); | 228 StringPiece fragment1(body.data(), split_point); |
| 231 QuicStreamFrame frame1(kClientDataStreamId1, false, 0, | 229 QuicStreamFrame frame1(kClientDataStreamId1, false, 0, |
| 232 StringPiece(fragment1)); | 230 StringPiece(fragment1)); |
| 233 stream_->OnStreamFrame(frame1); | 231 stream_->OnStreamFrame(frame1); |
| 234 | 232 |
| 235 StringPiece fragment2(body.data() + split_point, | 233 StringPiece fragment2(body.data() + split_point, body.size() - split_point); |
| 236 body.size() - split_point); | |
| 237 QuicStreamFrame frame2(kClientDataStreamId1, false, split_point, | 234 QuicStreamFrame frame2(kClientDataStreamId1, false, split_point, |
| 238 StringPiece(fragment2)); | 235 StringPiece(fragment2)); |
| 239 stream_->OnStreamFrame(frame2); | 236 stream_->OnStreamFrame(frame2); |
| 240 | 237 |
| 241 ASSERT_EQ(body, stream_->data()) << "split_point: " << split_point; | 238 ASSERT_EQ(body, stream_->data()) << "split_point: " << split_point; |
| 242 } | 239 } |
| 243 } | 240 } |
| 244 | 241 |
| 245 TEST_F(QuicDataStreamTest, ProcessHeadersAndBodyReadv) { | 242 TEST_F(QuicSpdyStreamTest, ProcessHeadersAndBodyReadv) { |
| 246 Initialize(!kShouldProcessData); | 243 Initialize(!kShouldProcessData); |
| 247 | 244 |
| 248 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); | 245 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); |
| 249 string body = "this is the body"; | 246 string body = "this is the body"; |
| 250 | 247 |
| 251 stream_->OnStreamHeaders(headers); | 248 stream_->OnStreamHeaders(headers); |
| 252 stream_->OnStreamHeadersComplete(false, headers.size()); | 249 stream_->OnStreamHeadersComplete(false, headers.size()); |
| 253 QuicStreamFrame frame(kClientDataStreamId1, false, 0, StringPiece(body)); | 250 QuicStreamFrame frame(kClientDataStreamId1, false, 0, StringPiece(body)); |
| 254 stream_->OnStreamFrame(frame); | 251 stream_->OnStreamFrame(frame); |
| 255 stream_->MarkHeadersConsumed(headers.length()); | 252 stream_->MarkHeadersConsumed(headers.length()); |
| 256 | 253 |
| 257 char buffer[2048]; | 254 char buffer[2048]; |
| 258 ASSERT_LT(body.length(), arraysize(buffer)); | 255 ASSERT_LT(body.length(), arraysize(buffer)); |
| 259 struct iovec vec; | 256 struct iovec vec; |
| 260 vec.iov_base = buffer; | 257 vec.iov_base = buffer; |
| 261 vec.iov_len = arraysize(buffer); | 258 vec.iov_len = arraysize(buffer); |
| 262 | 259 |
| 263 size_t bytes_read = stream_->Readv(&vec, 1); | 260 size_t bytes_read = stream_->Readv(&vec, 1); |
| 264 EXPECT_EQ(body.length(), bytes_read); | 261 EXPECT_EQ(body.length(), bytes_read); |
| 265 EXPECT_EQ(body, string(buffer, bytes_read)); | 262 EXPECT_EQ(body, string(buffer, bytes_read)); |
| 266 } | 263 } |
| 267 | 264 |
| 268 TEST_F(QuicDataStreamTest, ProcessHeadersAndBodyMarkConsumed) { | 265 TEST_F(QuicSpdyStreamTest, ProcessHeadersAndBodyMarkConsumed) { |
| 269 Initialize(!kShouldProcessData); | 266 Initialize(!kShouldProcessData); |
| 270 | 267 |
| 271 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); | 268 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); |
| 272 string body = "this is the body"; | 269 string body = "this is the body"; |
| 273 | 270 |
| 274 stream_->OnStreamHeaders(headers); | 271 stream_->OnStreamHeaders(headers); |
| 275 stream_->OnStreamHeadersComplete(false, headers.size()); | 272 stream_->OnStreamHeadersComplete(false, headers.size()); |
| 276 QuicStreamFrame frame(kClientDataStreamId1, false, 0, StringPiece(body)); | 273 QuicStreamFrame frame(kClientDataStreamId1, false, 0, StringPiece(body)); |
| 277 stream_->OnStreamFrame(frame); | 274 stream_->OnStreamFrame(frame); |
| 278 stream_->MarkHeadersConsumed(headers.length()); | 275 stream_->MarkHeadersConsumed(headers.length()); |
| 279 | 276 |
| 280 struct iovec vec; | 277 struct iovec vec; |
| 281 | 278 |
| 282 EXPECT_EQ(1, stream_->GetReadableRegions(&vec, 1)); | 279 EXPECT_EQ(1, stream_->GetReadableRegions(&vec, 1)); |
| 283 EXPECT_EQ(body.length(), vec.iov_len); | 280 EXPECT_EQ(body.length(), vec.iov_len); |
| 284 EXPECT_EQ(body, string(static_cast<char*>(vec.iov_base), vec.iov_len)); | 281 EXPECT_EQ(body, string(static_cast<char*>(vec.iov_base), vec.iov_len)); |
| 285 | 282 |
| 286 stream_->MarkConsumed(body.length()); | 283 stream_->MarkConsumed(body.length()); |
| 287 EXPECT_EQ(body.length(), stream_->flow_controller()->bytes_consumed()); | 284 EXPECT_EQ(body.length(), stream_->flow_controller()->bytes_consumed()); |
| 288 } | 285 } |
| 289 | 286 |
| 290 TEST_F(QuicDataStreamTest, ProcessHeadersAndBodyIncrementalReadv) { | 287 TEST_F(QuicSpdyStreamTest, ProcessHeadersAndBodyIncrementalReadv) { |
| 291 Initialize(!kShouldProcessData); | 288 Initialize(!kShouldProcessData); |
| 292 | 289 |
| 293 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); | 290 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); |
| 294 string body = "this is the body"; | 291 string body = "this is the body"; |
| 295 stream_->OnStreamHeaders(headers); | 292 stream_->OnStreamHeaders(headers); |
| 296 stream_->OnStreamHeadersComplete(false, headers.size()); | 293 stream_->OnStreamHeadersComplete(false, headers.size()); |
| 297 QuicStreamFrame frame(kClientDataStreamId1, false, 0, StringPiece(body)); | 294 QuicStreamFrame frame(kClientDataStreamId1, false, 0, StringPiece(body)); |
| 298 stream_->OnStreamFrame(frame); | 295 stream_->OnStreamFrame(frame); |
| 299 stream_->MarkHeadersConsumed(headers.length()); | 296 stream_->MarkHeadersConsumed(headers.length()); |
| 300 | 297 |
| 301 char buffer[1]; | 298 char buffer[1]; |
| 302 struct iovec vec; | 299 struct iovec vec; |
| 303 vec.iov_base = buffer; | 300 vec.iov_base = buffer; |
| 304 vec.iov_len = arraysize(buffer); | 301 vec.iov_len = arraysize(buffer); |
| 305 | 302 |
| 306 for (size_t i = 0; i < body.length(); ++i) { | 303 for (size_t i = 0; i < body.length(); ++i) { |
| 307 size_t bytes_read = stream_->Readv(&vec, 1); | 304 size_t bytes_read = stream_->Readv(&vec, 1); |
| 308 ASSERT_EQ(1u, bytes_read); | 305 ASSERT_EQ(1u, bytes_read); |
| 309 EXPECT_EQ(body.data()[i], buffer[0]); | 306 EXPECT_EQ(body.data()[i], buffer[0]); |
| 310 } | 307 } |
| 311 } | 308 } |
| 312 | 309 |
| 313 TEST_F(QuicDataStreamTest, ProcessHeadersUsingReadvWithMultipleIovecs) { | 310 TEST_F(QuicSpdyStreamTest, ProcessHeadersUsingReadvWithMultipleIovecs) { |
| 314 Initialize(!kShouldProcessData); | 311 Initialize(!kShouldProcessData); |
| 315 | 312 |
| 316 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); | 313 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); |
| 317 string body = "this is the body"; | 314 string body = "this is the body"; |
| 318 stream_->OnStreamHeaders(headers); | 315 stream_->OnStreamHeaders(headers); |
| 319 stream_->OnStreamHeadersComplete(false, headers.size()); | 316 stream_->OnStreamHeadersComplete(false, headers.size()); |
| 320 QuicStreamFrame frame(kClientDataStreamId1, false, 0, StringPiece(body)); | 317 QuicStreamFrame frame(kClientDataStreamId1, false, 0, StringPiece(body)); |
| 321 stream_->OnStreamFrame(frame); | 318 stream_->OnStreamFrame(frame); |
| 322 stream_->MarkHeadersConsumed(headers.length()); | 319 stream_->MarkHeadersConsumed(headers.length()); |
| 323 | 320 |
| 324 char buffer1[1]; | 321 char buffer1[1]; |
| 325 char buffer2[1]; | 322 char buffer2[1]; |
| 326 struct iovec vec[2]; | 323 struct iovec vec[2]; |
| 327 vec[0].iov_base = buffer1; | 324 vec[0].iov_base = buffer1; |
| 328 vec[0].iov_len = arraysize(buffer1); | 325 vec[0].iov_len = arraysize(buffer1); |
| 329 vec[1].iov_base = buffer2; | 326 vec[1].iov_base = buffer2; |
| 330 vec[1].iov_len = arraysize(buffer2); | 327 vec[1].iov_len = arraysize(buffer2); |
| 331 | 328 |
| 332 for (size_t i = 0; i < body.length(); i += 2) { | 329 for (size_t i = 0; i < body.length(); i += 2) { |
| 333 size_t bytes_read = stream_->Readv(vec, 2); | 330 size_t bytes_read = stream_->Readv(vec, 2); |
| 334 ASSERT_EQ(2u, bytes_read) << i; | 331 ASSERT_EQ(2u, bytes_read) << i; |
| 335 ASSERT_EQ(body.data()[i], buffer1[0]) << i; | 332 ASSERT_EQ(body.data()[i], buffer1[0]) << i; |
| 336 ASSERT_EQ(body.data()[i + 1], buffer2[0]) << i; | 333 ASSERT_EQ(body.data()[i + 1], buffer2[0]) << i; |
| 337 } | 334 } |
| 338 } | 335 } |
| 339 | 336 |
| 340 TEST_F(QuicDataStreamTest, StreamFlowControlBlocked) { | 337 TEST_F(QuicSpdyStreamTest, StreamFlowControlBlocked) { |
| 341 // Tests that we send a BLOCKED frame to the peer when we attempt to write, | 338 // Tests that we send a BLOCKED frame to the peer when we attempt to write, |
| 342 // but are flow control blocked. | 339 // but are flow control blocked. |
| 343 Initialize(kShouldProcessData); | 340 Initialize(kShouldProcessData); |
| 344 | 341 |
| 345 // Set a small flow control limit. | 342 // Set a small flow control limit. |
| 346 const uint64 kWindow = 36; | 343 const uint64 kWindow = 36; |
| 347 QuicFlowControllerPeer::SetSendWindowOffset(stream_->flow_controller(), | 344 QuicFlowControllerPeer::SetSendWindowOffset(stream_->flow_controller(), |
| 348 kWindow); | 345 kWindow); |
| 349 EXPECT_EQ(kWindow, QuicFlowControllerPeer::SendWindowOffset( | 346 EXPECT_EQ(kWindow, QuicFlowControllerPeer::SendWindowOffset( |
| 350 stream_->flow_controller())); | 347 stream_->flow_controller())); |
| 351 | 348 |
| 352 // Try to send more data than the flow control limit allows. | 349 // Try to send more data than the flow control limit allows. |
| 353 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); | 350 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); |
| 354 string body; | 351 string body; |
| 355 const uint64 kOverflow = 15; | 352 const uint64 kOverflow = 15; |
| 356 GenerateBody(&body, kWindow + kOverflow); | 353 GenerateBody(&body, kWindow + kOverflow); |
| 357 | 354 |
| 358 EXPECT_CALL(*connection_, SendBlocked(kClientDataStreamId1)); | 355 EXPECT_CALL(*connection_, SendBlocked(kClientDataStreamId1)); |
| 359 EXPECT_CALL(*session_, WritevData(kClientDataStreamId1, _, _, _, _, _)) | 356 EXPECT_CALL(*session_, WritevData(kClientDataStreamId1, _, _, _, _, _)) |
| 360 .WillOnce(Return(QuicConsumedData(kWindow, true))); | 357 .WillOnce(Return(QuicConsumedData(kWindow, true))); |
| 361 stream_->WriteOrBufferData(body, false, nullptr); | 358 stream_->WriteOrBufferData(body, false, nullptr); |
| 362 | 359 |
| 363 // Should have sent as much as possible, resulting in no send window left. | 360 // Should have sent as much as possible, resulting in no send window left. |
| 364 EXPECT_EQ(0u, | 361 EXPECT_EQ(0u, |
| 365 QuicFlowControllerPeer::SendWindowSize(stream_->flow_controller())); | 362 QuicFlowControllerPeer::SendWindowSize(stream_->flow_controller())); |
| 366 | 363 |
| 367 // And we should have queued the overflowed data. | 364 // And we should have queued the overflowed data. |
| 368 EXPECT_EQ(kOverflow, | 365 EXPECT_EQ(kOverflow, ReliableQuicStreamPeer::SizeOfQueuedData(stream_.get())); |
| 369 ReliableQuicStreamPeer::SizeOfQueuedData(stream_.get())); | |
| 370 } | 366 } |
| 371 | 367 |
| 372 TEST_F(QuicDataStreamTest, StreamFlowControlNoWindowUpdateIfNotConsumed) { | 368 TEST_F(QuicSpdyStreamTest, StreamFlowControlNoWindowUpdateIfNotConsumed) { |
| 373 // The flow control receive window decreases whenever we add new bytes to the | 369 // The flow control receive window decreases whenever we add new bytes to the |
| 374 // sequencer, whether they are consumed immediately or buffered. However we | 370 // sequencer, whether they are consumed immediately or buffered. However we |
| 375 // only send WINDOW_UPDATE frames based on increasing number of bytes | 371 // only send WINDOW_UPDATE frames based on increasing number of bytes |
| 376 // consumed. | 372 // consumed. |
| 377 | 373 |
| 378 // Don't process data - it will be buffered instead. | 374 // Don't process data - it will be buffered instead. |
| 379 Initialize(!kShouldProcessData); | 375 Initialize(!kShouldProcessData); |
| 380 | 376 |
| 381 // Expect no WINDOW_UPDATE frames to be sent. | 377 // Expect no WINDOW_UPDATE frames to be sent. |
| 382 EXPECT_CALL(*connection_, SendWindowUpdate(_, _)).Times(0); | 378 EXPECT_CALL(*connection_, SendWindowUpdate(_, _)).Times(0); |
| (...skipping 23 matching lines...) Expand all Loading... |
| 406 // half full. This should all be buffered, decreasing the receive window but | 402 // half full. This should all be buffered, decreasing the receive window but |
| 407 // not sending WINDOW_UPDATE. | 403 // not sending WINDOW_UPDATE. |
| 408 QuicStreamFrame frame2(kClientDataStreamId1, false, kWindow / 3, | 404 QuicStreamFrame frame2(kClientDataStreamId1, false, kWindow / 3, |
| 409 StringPiece(body)); | 405 StringPiece(body)); |
| 410 stream_->OnStreamFrame(frame2); | 406 stream_->OnStreamFrame(frame2); |
| 411 EXPECT_EQ( | 407 EXPECT_EQ( |
| 412 kWindow - (2 * kWindow / 3), | 408 kWindow - (2 * kWindow / 3), |
| 413 QuicFlowControllerPeer::ReceiveWindowSize(stream_->flow_controller())); | 409 QuicFlowControllerPeer::ReceiveWindowSize(stream_->flow_controller())); |
| 414 } | 410 } |
| 415 | 411 |
| 416 TEST_F(QuicDataStreamTest, StreamFlowControlWindowUpdate) { | 412 TEST_F(QuicSpdyStreamTest, StreamFlowControlWindowUpdate) { |
| 417 // Tests that on receipt of data, the stream updates its receive window offset | 413 // Tests that on receipt of data, the stream updates its receive window offset |
| 418 // appropriately, and sends WINDOW_UPDATE frames when its receive window drops | 414 // appropriately, and sends WINDOW_UPDATE frames when its receive window drops |
| 419 // too low. | 415 // too low. |
| 420 Initialize(kShouldProcessData); | 416 Initialize(kShouldProcessData); |
| 421 | 417 |
| 422 // Set a small flow control limit. | 418 // Set a small flow control limit. |
| 423 const uint64 kWindow = 36; | 419 const uint64 kWindow = 36; |
| 424 QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), | 420 QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), |
| 425 kWindow); | 421 kWindow); |
| 426 QuicFlowControllerPeer::SetMaxReceiveWindow(stream_->flow_controller(), | 422 QuicFlowControllerPeer::SetMaxReceiveWindow(stream_->flow_controller(), |
| (...skipping 23 matching lines...) Expand all Loading... |
| 450 EXPECT_CALL(*connection_, | 446 EXPECT_CALL(*connection_, |
| 451 SendWindowUpdate(kClientDataStreamId1, | 447 SendWindowUpdate(kClientDataStreamId1, |
| 452 QuicFlowControllerPeer::ReceiveWindowOffset( | 448 QuicFlowControllerPeer::ReceiveWindowOffset( |
| 453 stream_->flow_controller()) + | 449 stream_->flow_controller()) + |
| 454 2 * kWindow / 3)); | 450 2 * kWindow / 3)); |
| 455 stream_->OnStreamFrame(frame2); | 451 stream_->OnStreamFrame(frame2); |
| 456 EXPECT_EQ(kWindow, QuicFlowControllerPeer::ReceiveWindowSize( | 452 EXPECT_EQ(kWindow, QuicFlowControllerPeer::ReceiveWindowSize( |
| 457 stream_->flow_controller())); | 453 stream_->flow_controller())); |
| 458 } | 454 } |
| 459 | 455 |
| 460 TEST_F(QuicDataStreamTest, ConnectionFlowControlWindowUpdate) { | 456 TEST_F(QuicSpdyStreamTest, ConnectionFlowControlWindowUpdate) { |
| 461 // Tests that on receipt of data, the connection updates its receive window | 457 // Tests that on receipt of data, the connection updates its receive window |
| 462 // offset appropriately, and sends WINDOW_UPDATE frames when its receive | 458 // offset appropriately, and sends WINDOW_UPDATE frames when its receive |
| 463 // window drops too low. | 459 // window drops too low. |
| 464 Initialize(kShouldProcessData); | 460 Initialize(kShouldProcessData); |
| 465 | 461 |
| 466 // Set a small flow control limit for streams and connection. | 462 // Set a small flow control limit for streams and connection. |
| 467 const uint64 kWindow = 36; | 463 const uint64 kWindow = 36; |
| 468 QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), | 464 QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), |
| 469 kWindow); | 465 kWindow); |
| 470 QuicFlowControllerPeer::SetMaxReceiveWindow(stream_->flow_controller(), | 466 QuicFlowControllerPeer::SetMaxReceiveWindow(stream_->flow_controller(), |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 503 EXPECT_CALL(*connection_, SendWindowUpdate(kClientDataStreamId2, _)).Times(0); | 499 EXPECT_CALL(*connection_, SendWindowUpdate(kClientDataStreamId2, _)).Times(0); |
| 504 EXPECT_CALL(*connection_, | 500 EXPECT_CALL(*connection_, |
| 505 SendWindowUpdate(0, QuicFlowControllerPeer::ReceiveWindowOffset( | 501 SendWindowUpdate(0, QuicFlowControllerPeer::ReceiveWindowOffset( |
| 506 session_->flow_controller()) + | 502 session_->flow_controller()) + |
| 507 1 + kWindow / 2)); | 503 1 + kWindow / 2)); |
| 508 QuicStreamFrame frame3(kClientDataStreamId1, false, (kWindow / 4), | 504 QuicStreamFrame frame3(kClientDataStreamId1, false, (kWindow / 4), |
| 509 StringPiece("a")); | 505 StringPiece("a")); |
| 510 stream_->OnStreamFrame(frame3); | 506 stream_->OnStreamFrame(frame3); |
| 511 } | 507 } |
| 512 | 508 |
| 513 TEST_F(QuicDataStreamTest, StreamFlowControlViolation) { | 509 TEST_F(QuicSpdyStreamTest, StreamFlowControlViolation) { |
| 514 // Tests that on if the peer sends too much data (i.e. violates the flow | 510 // Tests that on if the peer sends too much data (i.e. violates the flow |
| 515 // control protocol), then we terminate the connection. | 511 // control protocol), then we terminate the connection. |
| 516 | 512 |
| 517 // Stream should not process data, so that data gets buffered in the | 513 // Stream should not process data, so that data gets buffered in the |
| 518 // sequencer, triggering flow control limits. | 514 // sequencer, triggering flow control limits. |
| 519 Initialize(!kShouldProcessData); | 515 Initialize(!kShouldProcessData); |
| 520 | 516 |
| 521 // Set a small flow control limit. | 517 // Set a small flow control limit. |
| 522 const uint64 kWindow = 50; | 518 const uint64 kWindow = 50; |
| 523 QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), | 519 QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), |
| 524 kWindow); | 520 kWindow); |
| 525 | 521 |
| 526 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); | 522 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); |
| 527 stream_->OnStreamHeaders(headers); | 523 stream_->OnStreamHeaders(headers); |
| 528 stream_->OnStreamHeadersComplete(false, headers.size()); | 524 stream_->OnStreamHeadersComplete(false, headers.size()); |
| 529 | 525 |
| 530 // Receive data to overflow the window, violating flow control. | 526 // Receive data to overflow the window, violating flow control. |
| 531 string body; | 527 string body; |
| 532 GenerateBody(&body, kWindow + 1); | 528 GenerateBody(&body, kWindow + 1); |
| 533 QuicStreamFrame frame(kClientDataStreamId1, false, 0, StringPiece(body)); | 529 QuicStreamFrame frame(kClientDataStreamId1, false, 0, StringPiece(body)); |
| 534 EXPECT_CALL(*connection_, | 530 EXPECT_CALL(*connection_, |
| 535 SendConnectionClose(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA)); | 531 SendConnectionClose(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA)); |
| 536 stream_->OnStreamFrame(frame); | 532 stream_->OnStreamFrame(frame); |
| 537 } | 533 } |
| 538 | 534 |
| 539 TEST_F(QuicDataStreamTest, ConnectionFlowControlViolation) { | 535 TEST_F(QuicSpdyStreamTest, ConnectionFlowControlViolation) { |
| 540 // Tests that on if the peer sends too much data (i.e. violates the flow | 536 // Tests that on if the peer sends too much data (i.e. violates the flow |
| 541 // control protocol), at the connection level (rather than the stream level) | 537 // control protocol), at the connection level (rather than the stream level) |
| 542 // then we terminate the connection. | 538 // then we terminate the connection. |
| 543 | 539 |
| 544 // Stream should not process data, so that data gets buffered in the | 540 // Stream should not process data, so that data gets buffered in the |
| 545 // sequencer, triggering flow control limits. | 541 // sequencer, triggering flow control limits. |
| 546 Initialize(!kShouldProcessData); | 542 Initialize(!kShouldProcessData); |
| 547 | 543 |
| 548 // Set a small flow control window on streams, and connection. | 544 // Set a small flow control window on streams, and connection. |
| 549 const uint64 kStreamWindow = 50; | 545 const uint64 kStreamWindow = 50; |
| 550 const uint64 kConnectionWindow = 10; | 546 const uint64 kConnectionWindow = 10; |
| 551 QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), | 547 QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), |
| 552 kStreamWindow); | 548 kStreamWindow); |
| 553 QuicFlowControllerPeer::SetReceiveWindowOffset(session_->flow_controller(), | 549 QuicFlowControllerPeer::SetReceiveWindowOffset(session_->flow_controller(), |
| 554 kConnectionWindow); | 550 kConnectionWindow); |
| 555 | 551 |
| 556 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); | 552 string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); |
| 557 stream_->OnStreamHeaders(headers); | 553 stream_->OnStreamHeaders(headers); |
| 558 stream_->OnStreamHeadersComplete(false, headers.size()); | 554 stream_->OnStreamHeadersComplete(false, headers.size()); |
| 559 | 555 |
| 560 // Send enough data to overflow the connection level flow control window. | 556 // Send enough data to overflow the connection level flow control window. |
| 561 string body; | 557 string body; |
| 562 GenerateBody(&body, kConnectionWindow + 1); | 558 GenerateBody(&body, kConnectionWindow + 1); |
| 563 EXPECT_LT(body.size(), kStreamWindow); | 559 EXPECT_LT(body.size(), kStreamWindow); |
| 564 QuicStreamFrame frame(kClientDataStreamId1, false, 0, StringPiece(body)); | 560 QuicStreamFrame frame(kClientDataStreamId1, false, 0, StringPiece(body)); |
| 565 | 561 |
| 566 EXPECT_CALL(*connection_, | 562 EXPECT_CALL(*connection_, |
| 567 SendConnectionClose(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA)); | 563 SendConnectionClose(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA)); |
| 568 stream_->OnStreamFrame(frame); | 564 stream_->OnStreamFrame(frame); |
| 569 } | 565 } |
| 570 | 566 |
| 571 TEST_F(QuicDataStreamTest, StreamFlowControlFinNotBlocked) { | 567 TEST_F(QuicSpdyStreamTest, StreamFlowControlFinNotBlocked) { |
| 572 // An attempt to write a FIN with no data should not be flow control blocked, | 568 // An attempt to write a FIN with no data should not be flow control blocked, |
| 573 // even if the send window is 0. | 569 // even if the send window is 0. |
| 574 | 570 |
| 575 Initialize(kShouldProcessData); | 571 Initialize(kShouldProcessData); |
| 576 | 572 |
| 577 // Set a flow control limit of zero. | 573 // Set a flow control limit of zero. |
| 578 QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), 0); | 574 QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), 0); |
| 579 EXPECT_EQ(0u, QuicFlowControllerPeer::ReceiveWindowOffset( | 575 EXPECT_EQ(0u, QuicFlowControllerPeer::ReceiveWindowOffset( |
| 580 stream_->flow_controller())); | 576 stream_->flow_controller())); |
| 581 | 577 |
| 582 // Send a frame with a FIN but no data. This should not be blocked. | 578 // Send a frame with a FIN but no data. This should not be blocked. |
| 583 string body = ""; | 579 string body = ""; |
| 584 bool fin = true; | 580 bool fin = true; |
| 585 | 581 |
| 586 EXPECT_CALL(*connection_, SendBlocked(kClientDataStreamId1)).Times(0); | 582 EXPECT_CALL(*connection_, SendBlocked(kClientDataStreamId1)).Times(0); |
| 587 EXPECT_CALL(*session_, WritevData(kClientDataStreamId1, _, _, _, _, _)) | 583 EXPECT_CALL(*session_, WritevData(kClientDataStreamId1, _, _, _, _, _)) |
| 588 .WillOnce(Return(QuicConsumedData(0, fin))); | 584 .WillOnce(Return(QuicConsumedData(0, fin))); |
| 589 | 585 |
| 590 stream_->WriteOrBufferData(body, fin, nullptr); | 586 stream_->WriteOrBufferData(body, fin, nullptr); |
| 591 } | 587 } |
| 592 | 588 |
| 593 } // namespace | 589 } // namespace |
| 594 } // namespace test | 590 } // namespace test |
| 595 } // namespace net | 591 } // namespace net |
| OLD | NEW |