| OLD | NEW |
| (Empty) |
| 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/spdy/buffered_spdy_framer.h" | |
| 6 | |
| 7 #include "net/spdy/spdy_test_util_common.h" | |
| 8 #include "testing/platform_test.h" | |
| 9 | |
| 10 namespace net { | |
| 11 | |
| 12 namespace { | |
| 13 | |
| 14 class TestBufferedSpdyVisitor : public BufferedSpdyFramerVisitorInterface { | |
| 15 public: | |
| 16 explicit TestBufferedSpdyVisitor(SpdyMajorVersion spdy_version) | |
| 17 : buffered_spdy_framer_(spdy_version, true), | |
| 18 error_count_(0), | |
| 19 setting_count_(0), | |
| 20 syn_frame_count_(0), | |
| 21 syn_reply_frame_count_(0), | |
| 22 headers_frame_count_(0), | |
| 23 push_promise_frame_count_(0), | |
| 24 header_stream_id_(static_cast<SpdyStreamId>(-1)), | |
| 25 promised_stream_id_(static_cast<SpdyStreamId>(-1)) { | |
| 26 } | |
| 27 | |
| 28 void OnError(SpdyFramer::SpdyError error_code) override { | |
| 29 LOG(INFO) << "SpdyFramer Error: " << error_code; | |
| 30 error_count_++; | |
| 31 } | |
| 32 | |
| 33 void OnStreamError(SpdyStreamId stream_id, | |
| 34 const std::string& description) override { | |
| 35 LOG(INFO) << "SpdyFramer Error on stream: " << stream_id << " " | |
| 36 << description; | |
| 37 error_count_++; | |
| 38 } | |
| 39 | |
| 40 void OnSynStream(SpdyStreamId stream_id, | |
| 41 SpdyStreamId associated_stream_id, | |
| 42 SpdyPriority priority, | |
| 43 bool fin, | |
| 44 bool unidirectional, | |
| 45 const SpdyHeaderBlock& headers) override { | |
| 46 header_stream_id_ = stream_id; | |
| 47 EXPECT_NE(header_stream_id_, SpdyFramer::kInvalidStream); | |
| 48 syn_frame_count_++; | |
| 49 headers_ = headers; | |
| 50 } | |
| 51 | |
| 52 void OnSynReply(SpdyStreamId stream_id, | |
| 53 bool fin, | |
| 54 const SpdyHeaderBlock& headers) override { | |
| 55 header_stream_id_ = stream_id; | |
| 56 EXPECT_NE(header_stream_id_, SpdyFramer::kInvalidStream); | |
| 57 syn_reply_frame_count_++; | |
| 58 headers_ = headers; | |
| 59 } | |
| 60 | |
| 61 void OnHeaders(SpdyStreamId stream_id, | |
| 62 bool has_priority, | |
| 63 SpdyPriority priority, | |
| 64 bool fin, | |
| 65 const SpdyHeaderBlock& headers) override { | |
| 66 header_stream_id_ = stream_id; | |
| 67 EXPECT_NE(header_stream_id_, SpdyFramer::kInvalidStream); | |
| 68 headers_frame_count_++; | |
| 69 headers_ = headers; | |
| 70 } | |
| 71 | |
| 72 void OnDataFrameHeader(SpdyStreamId stream_id, | |
| 73 size_t length, | |
| 74 bool fin) override { | |
| 75 ADD_FAILURE() << "Unexpected OnDataFrameHeader call."; | |
| 76 } | |
| 77 | |
| 78 void OnStreamFrameData(SpdyStreamId stream_id, | |
| 79 const char* data, | |
| 80 size_t len, | |
| 81 bool fin) override { | |
| 82 LOG(FATAL) << "Unexpected OnStreamFrameData call."; | |
| 83 } | |
| 84 | |
| 85 void OnSettings(bool clear_persisted) override {} | |
| 86 | |
| 87 void OnSetting(SpdySettingsIds id, uint8 flags, uint32 value) override { | |
| 88 setting_count_++; | |
| 89 } | |
| 90 | |
| 91 void OnPing(SpdyPingId unique_id, bool is_ack) override {} | |
| 92 | |
| 93 void OnRstStream(SpdyStreamId stream_id, | |
| 94 SpdyRstStreamStatus status) override {} | |
| 95 | |
| 96 void OnGoAway(SpdyStreamId last_accepted_stream_id, | |
| 97 SpdyGoAwayStatus status) override {} | |
| 98 | |
| 99 bool OnCredentialFrameData(const char*, size_t) { | |
| 100 LOG(FATAL) << "Unexpected OnCredentialFrameData call."; | |
| 101 return false; | |
| 102 } | |
| 103 | |
| 104 void OnDataFrameHeader(const SpdyFrame* frame) { | |
| 105 LOG(FATAL) << "Unexpected OnDataFrameHeader call."; | |
| 106 } | |
| 107 | |
| 108 void OnRstStream(const SpdyFrame& frame) {} | |
| 109 void OnGoAway(const SpdyFrame& frame) {} | |
| 110 void OnPing(const SpdyFrame& frame) {} | |
| 111 void OnWindowUpdate(SpdyStreamId stream_id, | |
| 112 uint32 delta_window_size) override {} | |
| 113 | |
| 114 void OnPushPromise(SpdyStreamId stream_id, | |
| 115 SpdyStreamId promised_stream_id, | |
| 116 const SpdyHeaderBlock& headers) override { | |
| 117 header_stream_id_ = stream_id; | |
| 118 EXPECT_NE(header_stream_id_, SpdyFramer::kInvalidStream); | |
| 119 push_promise_frame_count_++; | |
| 120 promised_stream_id_ = promised_stream_id; | |
| 121 EXPECT_NE(promised_stream_id_, SpdyFramer::kInvalidStream); | |
| 122 headers_ = headers; | |
| 123 } | |
| 124 | |
| 125 bool OnUnknownFrame(SpdyStreamId stream_id, int frame_type) override { | |
| 126 return true; | |
| 127 } | |
| 128 | |
| 129 void OnCredential(const SpdyFrame& frame) {} | |
| 130 | |
| 131 // Convenience function which runs a framer simulation with particular input. | |
| 132 void SimulateInFramer(const unsigned char* input, size_t size) { | |
| 133 buffered_spdy_framer_.set_visitor(this); | |
| 134 size_t input_remaining = size; | |
| 135 const char* input_ptr = reinterpret_cast<const char*>(input); | |
| 136 while (input_remaining > 0 && | |
| 137 buffered_spdy_framer_.error_code() == SpdyFramer::SPDY_NO_ERROR) { | |
| 138 // To make the tests more interesting, we feed random (amd small) chunks | |
| 139 // into the framer. This simulates getting strange-sized reads from | |
| 140 // the socket. | |
| 141 const size_t kMaxReadSize = 32; | |
| 142 size_t bytes_read = | |
| 143 (rand() % std::min(input_remaining, kMaxReadSize)) + 1; | |
| 144 size_t bytes_processed = | |
| 145 buffered_spdy_framer_.ProcessInput(input_ptr, bytes_read); | |
| 146 input_remaining -= bytes_processed; | |
| 147 input_ptr += bytes_processed; | |
| 148 } | |
| 149 } | |
| 150 | |
| 151 BufferedSpdyFramer buffered_spdy_framer_; | |
| 152 | |
| 153 // Counters from the visitor callbacks. | |
| 154 int error_count_; | |
| 155 int setting_count_; | |
| 156 int syn_frame_count_; | |
| 157 int syn_reply_frame_count_; | |
| 158 int headers_frame_count_; | |
| 159 int push_promise_frame_count_; | |
| 160 | |
| 161 // Header block streaming state: | |
| 162 SpdyStreamId header_stream_id_; | |
| 163 SpdyStreamId promised_stream_id_; | |
| 164 | |
| 165 // Headers from OnSyn, OnSynReply, OnHeaders and OnPushPromise for | |
| 166 // verification. | |
| 167 SpdyHeaderBlock headers_; | |
| 168 }; | |
| 169 | |
| 170 } // namespace | |
| 171 | |
| 172 class BufferedSpdyFramerTest | |
| 173 : public PlatformTest, | |
| 174 public ::testing::WithParamInterface<NextProto> { | |
| 175 protected: | |
| 176 // Returns true if the two header blocks have equivalent content. | |
| 177 bool CompareHeaderBlocks(const SpdyHeaderBlock* expected, | |
| 178 const SpdyHeaderBlock* actual) { | |
| 179 if (expected->size() != actual->size()) { | |
| 180 LOG(ERROR) << "Expected " << expected->size() << " headers; actually got " | |
| 181 << actual->size() << "."; | |
| 182 return false; | |
| 183 } | |
| 184 for (SpdyHeaderBlock::const_iterator it = expected->begin(); | |
| 185 it != expected->end(); | |
| 186 ++it) { | |
| 187 SpdyHeaderBlock::const_iterator it2 = actual->find(it->first); | |
| 188 if (it2 == actual->end()) { | |
| 189 LOG(ERROR) << "Expected header name '" << it->first << "'."; | |
| 190 return false; | |
| 191 } | |
| 192 if (it->second.compare(it2->second) != 0) { | |
| 193 LOG(ERROR) << "Expected header named '" << it->first | |
| 194 << "' to have a value of '" << it->second | |
| 195 << "'. The actual value received was '" << it2->second | |
| 196 << "'."; | |
| 197 return false; | |
| 198 } | |
| 199 } | |
| 200 return true; | |
| 201 } | |
| 202 | |
| 203 SpdyMajorVersion spdy_version() { | |
| 204 return NextProtoToSpdyMajorVersion(GetParam()); | |
| 205 } | |
| 206 }; | |
| 207 | |
| 208 INSTANTIATE_TEST_CASE_P( | |
| 209 NextProto, | |
| 210 BufferedSpdyFramerTest, | |
| 211 testing::Values(kProtoSPDY31, kProtoSPDY4_14, kProtoSPDY4_15)); | |
| 212 | |
| 213 TEST_P(BufferedSpdyFramerTest, OnSetting) { | |
| 214 SpdyFramer framer(spdy_version()); | |
| 215 SpdySettingsIR settings_ir; | |
| 216 settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, false, false, 2); | |
| 217 settings_ir.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, false, false, 3); | |
| 218 scoped_ptr<SpdyFrame> control_frame(framer.SerializeSettings(settings_ir)); | |
| 219 TestBufferedSpdyVisitor visitor(spdy_version()); | |
| 220 | |
| 221 visitor.SimulateInFramer( | |
| 222 reinterpret_cast<unsigned char*>(control_frame->data()), | |
| 223 control_frame->size()); | |
| 224 EXPECT_EQ(0, visitor.error_count_); | |
| 225 EXPECT_EQ(2, visitor.setting_count_); | |
| 226 } | |
| 227 | |
| 228 TEST_P(BufferedSpdyFramerTest, ReadSynStreamHeaderBlock) { | |
| 229 if (spdy_version() > SPDY3) { | |
| 230 // SYN_STREAM not supported in SPDY>3. | |
| 231 return; | |
| 232 } | |
| 233 SpdyHeaderBlock headers; | |
| 234 headers["aa"] = "vv"; | |
| 235 headers["bb"] = "ww"; | |
| 236 BufferedSpdyFramer framer(spdy_version(), true); | |
| 237 scoped_ptr<SpdyFrame> control_frame( | |
| 238 framer.CreateSynStream(1, // stream_id | |
| 239 0, // associated_stream_id | |
| 240 1, // priority | |
| 241 CONTROL_FLAG_NONE, | |
| 242 &headers)); | |
| 243 EXPECT_TRUE(control_frame.get() != NULL); | |
| 244 | |
| 245 TestBufferedSpdyVisitor visitor(spdy_version()); | |
| 246 visitor.SimulateInFramer( | |
| 247 reinterpret_cast<unsigned char*>(control_frame.get()->data()), | |
| 248 control_frame.get()->size()); | |
| 249 EXPECT_EQ(0, visitor.error_count_); | |
| 250 EXPECT_EQ(1, visitor.syn_frame_count_); | |
| 251 EXPECT_EQ(0, visitor.syn_reply_frame_count_); | |
| 252 EXPECT_EQ(0, visitor.headers_frame_count_); | |
| 253 EXPECT_EQ(0, visitor.push_promise_frame_count_); | |
| 254 EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_)); | |
| 255 } | |
| 256 | |
| 257 TEST_P(BufferedSpdyFramerTest, ReadSynReplyHeaderBlock) { | |
| 258 if (spdy_version() > SPDY3) { | |
| 259 // SYN_REPLY not supported in SPDY>3. | |
| 260 return; | |
| 261 } | |
| 262 SpdyHeaderBlock headers; | |
| 263 headers["alpha"] = "beta"; | |
| 264 headers["gamma"] = "delta"; | |
| 265 BufferedSpdyFramer framer(spdy_version(), true); | |
| 266 scoped_ptr<SpdyFrame> control_frame( | |
| 267 framer.CreateSynReply(1, // stream_id | |
| 268 CONTROL_FLAG_NONE, | |
| 269 &headers)); | |
| 270 EXPECT_TRUE(control_frame.get() != NULL); | |
| 271 | |
| 272 TestBufferedSpdyVisitor visitor(spdy_version()); | |
| 273 visitor.SimulateInFramer( | |
| 274 reinterpret_cast<unsigned char*>(control_frame.get()->data()), | |
| 275 control_frame.get()->size()); | |
| 276 EXPECT_EQ(0, visitor.error_count_); | |
| 277 EXPECT_EQ(0, visitor.syn_frame_count_); | |
| 278 EXPECT_EQ(0, visitor.push_promise_frame_count_); | |
| 279 if (spdy_version() < SPDY4) { | |
| 280 EXPECT_EQ(1, visitor.syn_reply_frame_count_); | |
| 281 EXPECT_EQ(0, visitor.headers_frame_count_); | |
| 282 } else { | |
| 283 EXPECT_EQ(0, visitor.syn_reply_frame_count_); | |
| 284 EXPECT_EQ(1, visitor.headers_frame_count_); | |
| 285 } | |
| 286 EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_)); | |
| 287 } | |
| 288 | |
| 289 TEST_P(BufferedSpdyFramerTest, ReadHeadersHeaderBlock) { | |
| 290 SpdyHeaderBlock headers; | |
| 291 headers["alpha"] = "beta"; | |
| 292 headers["gamma"] = "delta"; | |
| 293 BufferedSpdyFramer framer(spdy_version(), true); | |
| 294 scoped_ptr<SpdyFrame> control_frame( | |
| 295 framer.CreateHeaders(1, // stream_id | |
| 296 CONTROL_FLAG_NONE, | |
| 297 0, // priority | |
| 298 &headers)); | |
| 299 EXPECT_TRUE(control_frame.get() != NULL); | |
| 300 | |
| 301 TestBufferedSpdyVisitor visitor(spdy_version()); | |
| 302 visitor.SimulateInFramer( | |
| 303 reinterpret_cast<unsigned char*>(control_frame.get()->data()), | |
| 304 control_frame.get()->size()); | |
| 305 EXPECT_EQ(0, visitor.error_count_); | |
| 306 EXPECT_EQ(0, visitor.syn_frame_count_); | |
| 307 EXPECT_EQ(0, visitor.syn_reply_frame_count_); | |
| 308 EXPECT_EQ(1, visitor.headers_frame_count_); | |
| 309 EXPECT_EQ(0, visitor.push_promise_frame_count_); | |
| 310 EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_)); | |
| 311 } | |
| 312 | |
| 313 TEST_P(BufferedSpdyFramerTest, ReadPushPromiseHeaderBlock) { | |
| 314 if (spdy_version() < SPDY4) | |
| 315 return; | |
| 316 SpdyHeaderBlock headers; | |
| 317 headers["alpha"] = "beta"; | |
| 318 headers["gamma"] = "delta"; | |
| 319 BufferedSpdyFramer framer(spdy_version(), true); | |
| 320 scoped_ptr<SpdyFrame> control_frame( | |
| 321 framer.CreatePushPromise(1, 2, &headers)); | |
| 322 EXPECT_TRUE(control_frame.get() != NULL); | |
| 323 | |
| 324 TestBufferedSpdyVisitor visitor(spdy_version()); | |
| 325 visitor.SimulateInFramer( | |
| 326 reinterpret_cast<unsigned char*>(control_frame.get()->data()), | |
| 327 control_frame.get()->size()); | |
| 328 EXPECT_EQ(0, visitor.error_count_); | |
| 329 EXPECT_EQ(0, visitor.syn_frame_count_); | |
| 330 EXPECT_EQ(0, visitor.syn_reply_frame_count_); | |
| 331 EXPECT_EQ(0, visitor.headers_frame_count_); | |
| 332 EXPECT_EQ(1, visitor.push_promise_frame_count_); | |
| 333 EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_)); | |
| 334 EXPECT_EQ(1u, visitor.header_stream_id_); | |
| 335 EXPECT_EQ(2u, visitor.promised_stream_id_); | |
| 336 } | |
| 337 | |
| 338 } // namespace net | |
| OLD | NEW |