1 // Copyright 2016 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 #ifndef QUICHE_SPDY_CORE_SPDY_DEFRAMER_VISITOR_H_ 6 #define QUICHE_SPDY_CORE_SPDY_DEFRAMER_VISITOR_H_ 7 8 // Supports testing by converting callbacks to SpdyFramerVisitorInterface into 9 // callbacks to SpdyDeframerVisitorInterface, whose arguments are generally 10 // SpdyFrameIR instances. This enables a test client or test backend to operate 11 // at a level between the low-level callbacks of SpdyFramerVisitorInterface and 12 // the much higher level of entire messages (i.e. headers, body, trailers). 13 // Where possible the converter (SpdyTestDeframer) tries to preserve information 14 // that might be useful to tests (e.g. the order of headers or the amount of 15 // padding); the design also aims to allow tests to be concise, ideally 16 // supporting gMock style EXPECT_CALL(visitor, OnHeaders(...matchers...)) 17 // without too much boilerplate. 18 // 19 // Only supports HTTP/2 for the moment. 20 // 21 // Example of usage: 22 // 23 // SpdyFramer framer(HTTP2); 24 // 25 // // Need to call SpdyTestDeframer::AtFrameEnd() after processing each 26 // // frame, so tell SpdyFramer to stop after each. 27 // framer.set_process_single_input_frame(true); 28 // 29 // // Need the new OnHeader callbacks. 30 // framer.set_use_new_methods_for_test(true); 31 // 32 // // Create your visitor, a subclass of SpdyDeframerVisitorInterface. 33 // // For example, using DeframerCallbackCollector to collect frames: 34 // std::vector<CollectedFrame> collected_frames; 35 // auto your_visitor = std::make_unique<DeframerCallbackCollector>( 36 // &collected_frames); 37 // 38 // // Transfer ownership of your visitor to the converter, which ensures that 39 // // your visitor stays alive while the converter needs to call it. 40 // auto the_deframer = SpdyTestDeframer::CreateConverter( 41 // std::move(your_visitor)); 42 // 43 // // Tell the framer to notify SpdyTestDeframer of the decoded frame 44 // // details. 45 // framer.set_visitor(the_deframer.get()); 46 // 47 // // Process frames. 48 // absl::string_view input = ... 49 // while (!input.empty() && !framer.HasError()) { 50 // size_t consumed = framer.ProcessInput(input.data(), input.size()); 51 // input.remove_prefix(consumed); 52 // if (framer.state() == SpdyFramer::SPDY_READY_FOR_FRAME) { 53 // the_deframer->AtFrameEnd(); 54 // } 55 // } 56 // 57 // // Make sure that the correct frames were received. For example: 58 // ASSERT_EQ(collected_frames.size(), 3); 59 // 60 // SpdyDataIR expected1(7 /*stream_id*/, "Data Payload"); 61 // expected1.set_padding_len(17); 62 // EXPECT_TRUE(collected_frames[0].VerifyEquals(expected1)); 63 // 64 // // Repeat for the other frames. 65 // 66 // Note that you could also seed the subclass of SpdyDeframerVisitorInterface 67 // with the expected frames, which it would pop-off the list as its expectations 68 // are met. 69 70 #include <cstdint> 71 #include <memory> 72 #include <string> 73 #include <type_traits> 74 #include <utility> 75 #include <vector> 76 77 #include "net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h" 78 #include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" 79 #include "net/third_party/quiche/src/spdy/core/spdy_protocol_test_utils.h" 80 #include "net/third_party/quiche/src/spdy/core/spdy_test_utils.h" 81 #include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h" 82 83 namespace spdy { 84 namespace test { 85 86 // Non-lossy representation of a SETTINGS frame payload. 87 typedef std::vector<std::pair<SpdyKnownSettingsId, uint32_t>> SettingVector; 88 89 // StringPairVector is used to record information lost by SpdyHeaderBlock, in 90 // particular the order of each header entry, though it doesn't expose the 91 // inner details of the HPACK block, such as the type of encoding selected 92 // for each header entry, nor dynamic table size changes. 93 typedef std::pair<std::string, std::string> StringPair; 94 typedef std::vector<StringPair> StringPairVector; 95 96 // Forward decl. 97 class SpdyTestDeframer; 98 99 // Note that this only roughly captures the frames, as padding bytes are lost, 100 // continuation frames are combined with their leading HEADERS or PUSH_PROMISE, 101 // the details of the HPACK encoding are lost, leaving 102 // only the list of header entries (name and value strings). If really helpful, 103 // we could add a SpdyRawDeframerVisitorInterface that gets the HPACK bytes, 104 // and receives continuation frames. For more info we'd need to improve 105 // SpdyFramerVisitorInterface. 106 class SpdyDeframerVisitorInterface { 107 public: ~SpdyDeframerVisitorInterface()108 virtual ~SpdyDeframerVisitorInterface() {} 109 110 // Wrap a visitor in another SpdyDeframerVisitorInterface that will 111 // DVLOG each call, and will then forward the calls to the wrapped visitor 112 // (if provided; nullptr is OK). Takes ownership of the wrapped visitor. 113 static std::unique_ptr<SpdyDeframerVisitorInterface> LogBeforeVisiting( 114 std::unique_ptr<SpdyDeframerVisitorInterface> wrapped_visitor); 115 OnAltSvc(std::unique_ptr<SpdyAltSvcIR>)116 virtual void OnAltSvc(std::unique_ptr<SpdyAltSvcIR> /*frame*/) {} OnData(std::unique_ptr<SpdyDataIR>)117 virtual void OnData(std::unique_ptr<SpdyDataIR> /*frame*/) {} OnGoAway(std::unique_ptr<SpdyGoAwayIR>)118 virtual void OnGoAway(std::unique_ptr<SpdyGoAwayIR> /*frame*/) {} 119 120 // SpdyHeadersIR and SpdyPushPromiseIR each has a SpdyHeaderBlock which 121 // significantly modifies the headers, so the actual header entries (name 122 // and value strings) are provided in a vector. OnHeaders(std::unique_ptr<SpdyHeadersIR>,std::unique_ptr<StringPairVector>)123 virtual void OnHeaders(std::unique_ptr<SpdyHeadersIR> /*frame*/, 124 std::unique_ptr<StringPairVector> /*headers*/) {} 125 OnPing(std::unique_ptr<SpdyPingIR>)126 virtual void OnPing(std::unique_ptr<SpdyPingIR> /*frame*/) {} 127 virtual void OnPingAck(std::unique_ptr<SpdyPingIR> /*frame*/); OnPriority(std::unique_ptr<SpdyPriorityIR>)128 virtual void OnPriority(std::unique_ptr<SpdyPriorityIR> /*frame*/) {} 129 130 // SpdyHeadersIR and SpdyPushPromiseIR each has a SpdyHeaderBlock which 131 // significantly modifies the headers, so the actual header entries (name 132 // and value strings) are provided in a vector. OnPushPromise(std::unique_ptr<SpdyPushPromiseIR>,std::unique_ptr<StringPairVector>)133 virtual void OnPushPromise(std::unique_ptr<SpdyPushPromiseIR> /*frame*/, 134 std::unique_ptr<StringPairVector> /*headers*/) {} 135 OnRstStream(std::unique_ptr<SpdyRstStreamIR>)136 virtual void OnRstStream(std::unique_ptr<SpdyRstStreamIR> /*frame*/) {} 137 138 // SpdySettingsIR has a map for settings, so loses info about the order of 139 // settings, and whether the same setting appeared more than once, so the 140 // the actual settings (parameter and value) are provided in a vector. OnSettings(std::unique_ptr<SpdySettingsIR>,std::unique_ptr<SettingVector>)141 virtual void OnSettings(std::unique_ptr<SpdySettingsIR> /*frame*/, 142 std::unique_ptr<SettingVector> /*settings*/) {} 143 144 // A settings frame with an ACK has no content, but for uniformity passing 145 // a frame with the ACK flag set. 146 virtual void OnSettingsAck(std::unique_ptr<SpdySettingsIR> /*frame*/); 147 OnWindowUpdate(std::unique_ptr<SpdyWindowUpdateIR>)148 virtual void OnWindowUpdate(std::unique_ptr<SpdyWindowUpdateIR> /*frame*/) {} 149 150 // The SpdyFramer will not process any more data at this point. OnError(http2::Http2DecoderAdapter::SpdyFramerError,SpdyTestDeframer *)151 virtual void OnError(http2::Http2DecoderAdapter::SpdyFramerError /*error*/, 152 SpdyTestDeframer* /*deframer*/) {} 153 }; 154 155 class SpdyTestDeframer : public SpdyFramerVisitorInterface { 156 public: ~SpdyTestDeframer()157 ~SpdyTestDeframer() override {} 158 159 // Creates a SpdyFramerVisitorInterface that builds SpdyFrameIR concrete 160 // instances based on the callbacks it receives; when an entire frame is 161 // decoded/reconstructed it calls the passed in SpdyDeframerVisitorInterface. 162 // Transfers ownership of visitor to the new SpdyTestDeframer, which ensures 163 // that it continues to exist while the SpdyTestDeframer exists. 164 static std::unique_ptr<SpdyTestDeframer> CreateConverter( 165 std::unique_ptr<SpdyDeframerVisitorInterface> visitor); 166 167 // Call to notify the deframer that the SpdyFramer has returned after reaching 168 // the end of decoding a frame. This is used to flush info about some frame 169 // types where we don't get a clear end signal; others are flushed (i.e. the 170 // appropriate call to the SpdyDeframerVisitorInterface method is invoked) 171 // as they're decoded by SpdyFramer and it calls the deframer. See the 172 // example in the comments at the top of this file. 173 virtual bool AtFrameEnd() = 0; 174 175 protected: SpdyTestDeframer()176 SpdyTestDeframer() {} 177 SpdyTestDeframer(const SpdyTestDeframer&) = delete; 178 SpdyTestDeframer& operator=(const SpdyTestDeframer&) = delete; 179 }; 180 181 // CollectedFrame holds the result of one call to SpdyDeframerVisitorInterface, 182 // as recorded by DeframerCallbackCollector. 183 struct CollectedFrame { 184 CollectedFrame(); 185 CollectedFrame(CollectedFrame&& other); 186 ~CollectedFrame(); 187 CollectedFrame& operator=(CollectedFrame&& other); 188 189 // Compare a SpdyFrameIR sub-class instance, expected_ir, against the 190 // collected SpdyFrameIR. 191 template <class T, 192 typename X = 193 typename std::enable_if<std::is_base_of<SpdyFrameIR, T>::value>> VerifyHasFrameCollectedFrame194 ::testing::AssertionResult VerifyHasFrame(const T& expected_ir) const { 195 return VerifySpdyFrameIREquals(expected_ir, frame_ir.get()); 196 } 197 198 // Compare the collected headers against a StringPairVector. Ignores 199 // this->frame_ir. 200 ::testing::AssertionResult VerifyHasHeaders( 201 const StringPairVector& expected_headers) const; 202 203 // Compare the collected settings (parameter and value pairs) against 204 // expected_settings. Ignores this->frame_ir. 205 ::testing::AssertionResult VerifyHasSettings( 206 const SettingVector& expected_settings) const; 207 208 std::unique_ptr<SpdyFrameIR> frame_ir; 209 std::unique_ptr<StringPairVector> headers; 210 std::unique_ptr<SettingVector> settings; 211 bool error_reported = false; 212 }; 213 214 // Creates a CollectedFrame instance for each callback, storing it in the 215 // vector provided to the constructor. 216 class DeframerCallbackCollector : public SpdyDeframerVisitorInterface { 217 public: 218 explicit DeframerCallbackCollector( 219 std::vector<CollectedFrame>* collected_frames); ~DeframerCallbackCollector()220 ~DeframerCallbackCollector() override {} 221 222 void OnAltSvc(std::unique_ptr<SpdyAltSvcIR> frame_ir) override; 223 void OnData(std::unique_ptr<SpdyDataIR> frame_ir) override; 224 void OnGoAway(std::unique_ptr<SpdyGoAwayIR> frame_ir) override; 225 void OnHeaders(std::unique_ptr<SpdyHeadersIR> frame_ir, 226 std::unique_ptr<StringPairVector> headers) override; 227 void OnPing(std::unique_ptr<SpdyPingIR> frame_ir) override; 228 void OnPingAck(std::unique_ptr<SpdyPingIR> frame_ir) override; 229 void OnPriority(std::unique_ptr<SpdyPriorityIR> frame_ir) override; 230 void OnPushPromise(std::unique_ptr<SpdyPushPromiseIR> frame_ir, 231 std::unique_ptr<StringPairVector> headers) override; 232 void OnRstStream(std::unique_ptr<SpdyRstStreamIR> frame_ir) override; 233 void OnSettings(std::unique_ptr<SpdySettingsIR> frame_ir, 234 std::unique_ptr<SettingVector> settings) override; 235 void OnSettingsAck(std::unique_ptr<SpdySettingsIR> frame_ir) override; 236 void OnWindowUpdate(std::unique_ptr<SpdyWindowUpdateIR> frame_ir) override; 237 void OnError(http2::Http2DecoderAdapter::SpdyFramerError error, 238 SpdyTestDeframer* deframer) override; 239 240 private: 241 std::vector<CollectedFrame>* collected_frames_; 242 }; 243 244 } // namespace test 245 } // namespace spdy 246 247 #endif // QUICHE_SPDY_CORE_SPDY_DEFRAMER_VISITOR_H_ 248