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