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 header_stream_id_(-1) { 24 } 25 26 virtual void OnError(SpdyFramer::SpdyError error_code) OVERRIDE { 27 LOG(INFO) << "SpdyFramer Error: " << error_code; 28 error_count_++; 29 } 30 31 virtual void OnStreamError( 32 SpdyStreamId stream_id, 33 const std::string& description) OVERRIDE { 34 LOG(INFO) << "SpdyFramer Error on stream: " << stream_id << " " 35 << description; 36 error_count_++; 37 } 38 39 virtual void OnSynStream(SpdyStreamId stream_id, 40 SpdyStreamId associated_stream_id, 41 SpdyPriority priority, 42 uint8 credential_slot, 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 virtual 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 virtual void OnHeaders(SpdyStreamId stream_id, 62 bool fin, 63 const SpdyHeaderBlock& headers) OVERRIDE { 64 header_stream_id_ = stream_id; 65 EXPECT_NE(header_stream_id_, SpdyFramer::kInvalidStream); 66 headers_frame_count_++; 67 headers_ = headers; 68 } 69 70 virtual void OnDataFrameHeader(SpdyStreamId stream_id, 71 size_t length, 72 bool fin) OVERRIDE { 73 ADD_FAILURE() << "Unexpected OnDataFrameHeader call."; 74 } 75 76 virtual void OnStreamFrameData(SpdyStreamId stream_id, 77 const char* data, 78 size_t len, 79 bool fin) OVERRIDE { 80 LOG(FATAL) << "Unexpected OnStreamFrameData call."; 81 } 82 83 virtual void OnSettings(bool clear_persisted) OVERRIDE {} 84 85 virtual void OnSetting(SpdySettingsIds id, 86 uint8 flags, 87 uint32 value) OVERRIDE { 88 setting_count_++; 89 } 90 91 virtual void OnPing(uint32 unique_id) OVERRIDE {} 92 93 virtual void OnRstStream(SpdyStreamId stream_id, 94 SpdyRstStreamStatus status) OVERRIDE { 95 } 96 97 virtual void OnGoAway(SpdyStreamId last_accepted_stream_id, 98 SpdyGoAwayStatus status) OVERRIDE { 99 } 100 101 bool OnCredentialFrameData(const char*, size_t) { 102 LOG(FATAL) << "Unexpected OnCredentialFrameData call."; 103 return false; 104 } 105 106 void OnDataFrameHeader(const SpdyFrame* frame) { 107 LOG(FATAL) << "Unexpected OnDataFrameHeader call."; 108 } 109 110 void OnRstStream(const SpdyFrame& frame) {} 111 void OnGoAway(const SpdyFrame& frame) {} 112 void OnPing(const SpdyFrame& frame) {} 113 virtual void OnWindowUpdate(SpdyStreamId stream_id, 114 uint32 delta_window_size) OVERRIDE {} 115 virtual void OnPushPromise(SpdyStreamId stream_id, 116 SpdyStreamId promised_stream_id) OVERRIDE {} 117 void OnCredential(const SpdyFrame& frame) {} 118 119 // Convenience function which runs a framer simulation with particular input. 120 void SimulateInFramer(const unsigned char* input, size_t size) { 121 buffered_spdy_framer_.set_visitor(this); 122 size_t input_remaining = size; 123 const char* input_ptr = reinterpret_cast<const char*>(input); 124 while (input_remaining > 0 && 125 buffered_spdy_framer_.error_code() == SpdyFramer::SPDY_NO_ERROR) { 126 // To make the tests more interesting, we feed random (amd small) chunks 127 // into the framer. This simulates getting strange-sized reads from 128 // the socket. 129 const size_t kMaxReadSize = 32; 130 size_t bytes_read = 131 (rand() % std::min(input_remaining, kMaxReadSize)) + 1; 132 size_t bytes_processed = 133 buffered_spdy_framer_.ProcessInput(input_ptr, bytes_read); 134 input_remaining -= bytes_processed; 135 input_ptr += bytes_processed; 136 } 137 } 138 139 BufferedSpdyFramer buffered_spdy_framer_; 140 141 // Counters from the visitor callbacks. 142 int error_count_; 143 int setting_count_; 144 int syn_frame_count_; 145 int syn_reply_frame_count_; 146 int headers_frame_count_; 147 148 // Header block streaming state: 149 SpdyStreamId header_stream_id_; 150 151 // Headers from OnSyn, OnSynReply and OnHeaders for verification. 152 SpdyHeaderBlock headers_; 153 }; 154 155 } // namespace 156 157 class BufferedSpdyFramerTest 158 : public PlatformTest, 159 public ::testing::WithParamInterface<NextProto> { 160 protected: 161 // Returns true if the two header blocks have equivalent content. 162 bool CompareHeaderBlocks(const SpdyHeaderBlock* expected, 163 const SpdyHeaderBlock* actual) { 164 if (expected->size() != actual->size()) { 165 LOG(ERROR) << "Expected " << expected->size() << " headers; actually got " 166 << actual->size() << "."; 167 return false; 168 } 169 for (SpdyHeaderBlock::const_iterator it = expected->begin(); 170 it != expected->end(); 171 ++it) { 172 SpdyHeaderBlock::const_iterator it2 = actual->find(it->first); 173 if (it2 == actual->end()) { 174 LOG(ERROR) << "Expected header name '" << it->first << "'."; 175 return false; 176 } 177 if (it->second.compare(it2->second) != 0) { 178 LOG(ERROR) << "Expected header named '" << it->first 179 << "' to have a value of '" << it->second 180 << "'. The actual value received was '" << it2->second 181 << "'."; 182 return false; 183 } 184 } 185 return true; 186 } 187 188 SpdyMajorVersion spdy_version() { 189 return NextProtoToSpdyMajorVersion(GetParam()); 190 } 191 }; 192 193 INSTANTIATE_TEST_CASE_P( 194 NextProto, 195 BufferedSpdyFramerTest, 196 testing::Values(kProtoDeprecatedSPDY2, 197 kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2, 198 kProtoHTTP2Draft04)); 199 200 TEST_P(BufferedSpdyFramerTest, OnSetting) { 201 SpdyFramer framer(spdy_version()); 202 SettingsMap settings; 203 settings[SETTINGS_UPLOAD_BANDWIDTH] = 204 SettingsFlagsAndValue(SETTINGS_FLAG_NONE, 0x00000002); 205 settings[SETTINGS_DOWNLOAD_BANDWIDTH] = 206 SettingsFlagsAndValue(SETTINGS_FLAG_NONE, 0x00000003); 207 208 scoped_ptr<SpdyFrame> control_frame(framer.CreateSettings(settings)); 209 TestBufferedSpdyVisitor visitor(spdy_version()); 210 211 visitor.SimulateInFramer( 212 reinterpret_cast<unsigned char*>(control_frame->data()), 213 control_frame->size()); 214 EXPECT_EQ(0, visitor.error_count_); 215 EXPECT_EQ(2, visitor.setting_count_); 216 } 217 218 TEST_P(BufferedSpdyFramerTest, ReadSynStreamHeaderBlock) { 219 SpdyHeaderBlock headers; 220 headers["aa"] = "vv"; 221 headers["bb"] = "ww"; 222 BufferedSpdyFramer framer(spdy_version(), true); 223 scoped_ptr<SpdyFrame> control_frame( 224 framer.CreateSynStream(1, // stream_id 225 0, // associated_stream_id 226 1, // priority 227 0, // credential_slot 228 CONTROL_FLAG_NONE, 229 &headers)); 230 EXPECT_TRUE(control_frame.get() != NULL); 231 232 TestBufferedSpdyVisitor visitor(spdy_version()); 233 visitor.SimulateInFramer( 234 reinterpret_cast<unsigned char*>(control_frame.get()->data()), 235 control_frame.get()->size()); 236 EXPECT_EQ(0, visitor.error_count_); 237 EXPECT_EQ(1, visitor.syn_frame_count_); 238 EXPECT_EQ(0, visitor.syn_reply_frame_count_); 239 EXPECT_EQ(0, visitor.headers_frame_count_); 240 EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_)); 241 } 242 243 TEST_P(BufferedSpdyFramerTest, ReadSynReplyHeaderBlock) { 244 SpdyHeaderBlock headers; 245 headers["alpha"] = "beta"; 246 headers["gamma"] = "delta"; 247 BufferedSpdyFramer framer(spdy_version(), true); 248 scoped_ptr<SpdyFrame> control_frame( 249 framer.CreateSynReply(1, // stream_id 250 CONTROL_FLAG_NONE, 251 &headers)); 252 EXPECT_TRUE(control_frame.get() != NULL); 253 254 TestBufferedSpdyVisitor visitor(spdy_version()); 255 visitor.SimulateInFramer( 256 reinterpret_cast<unsigned char*>(control_frame.get()->data()), 257 control_frame.get()->size()); 258 EXPECT_EQ(0, visitor.error_count_); 259 EXPECT_EQ(0, visitor.syn_frame_count_); 260 EXPECT_EQ(1, visitor.syn_reply_frame_count_); 261 EXPECT_EQ(0, visitor.headers_frame_count_); 262 EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_)); 263 } 264 265 TEST_P(BufferedSpdyFramerTest, ReadHeadersHeaderBlock) { 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.CreateHeaders(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.syn_reply_frame_count_); 283 EXPECT_EQ(1, visitor.headers_frame_count_); 284 EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_)); 285 } 286 287 } // namespace net 288