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