OLD | NEW |
(Empty) | |
| 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 NET_SPDY_SPDY_DEFRAMER_VISITOR_H_ |
| 6 #define NET_SPDY_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 = gtl::MakeUnique<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 // StringPiece 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 <stdint.h> |
| 71 |
| 72 #include <memory> |
| 73 #include <string> |
| 74 #include <type_traits> |
| 75 #include <utility> |
| 76 #include <vector> |
| 77 |
| 78 #include "base/logging.h" |
| 79 #include "base/macros.h" |
| 80 #include "net/spdy/spdy_framer.h" |
| 81 #include "net/spdy/spdy_protocol.h" |
| 82 #include "net/spdy/spdy_protocol_test_utils.h" |
| 83 #include "net/spdy/spdy_test_utils.h" |
| 84 |
| 85 namespace net { |
| 86 namespace test { |
| 87 |
| 88 // Non-lossy representation of a SETTINGS frame payload. |
| 89 typedef std::vector<std::pair<SpdySettingsIds, uint32_t>> SettingVector; |
| 90 |
| 91 // StringPairVector is used to record information lost by SpdyHeaderBlock, in |
| 92 // particular the order of each header entry, though it doesn't expose the |
| 93 // inner details of the HPACK block, such as the type of encoding selected |
| 94 // for each header entry, nor dynamic table size changes. |
| 95 typedef std::pair<std::string, std::string> StringPair; |
| 96 typedef std::vector<StringPair> StringPairVector; |
| 97 |
| 98 // Forward decl. |
| 99 class SpdyTestDeframer; |
| 100 |
| 101 // Note that this only roughly captures the frames, as padding bytes are lost, |
| 102 // continuation frames are combined with their leading HEADERS or PUSH_PROMISE, |
| 103 // the details of the HPACK encoding are lost, leaving |
| 104 // only the list of header entries (name and value strings). If really helpful, |
| 105 // we could add a SpdyRawDeframerVisitorInterface that gets the HPACK bytes, |
| 106 // and receives continuation frames. For more info we'd need to improve |
| 107 // SpdyFramerVisitorInterface. |
| 108 class SpdyDeframerVisitorInterface { |
| 109 public: |
| 110 virtual ~SpdyDeframerVisitorInterface() {} |
| 111 |
| 112 // Wrap a visitor in another SpdyDeframerVisitorInterface that will |
| 113 // DVLOG each call, and will then forward the calls to the wrapped visitor |
| 114 // (if provided; nullptr is OK). Takes ownership of the wrapped visitor. |
| 115 static std::unique_ptr<SpdyDeframerVisitorInterface> LogBeforeVisiting( |
| 116 std::unique_ptr<SpdyDeframerVisitorInterface> wrapped_visitor); |
| 117 |
| 118 virtual void OnAltSvc(std::unique_ptr<SpdyAltSvcIR> frame) {} |
| 119 virtual void OnData(std::unique_ptr<SpdyDataIR> frame) {} |
| 120 virtual void OnGoAway(std::unique_ptr<SpdyGoAwayIR> frame) {} |
| 121 |
| 122 // SpdyHeadersIR and SpdyPushPromiseIR each has a SpdyHeaderBlock which |
| 123 // significantly modifies the headers, so the actual header entries (name |
| 124 // and value strings) are provided in a vector. |
| 125 virtual void OnHeaders(std::unique_ptr<SpdyHeadersIR> frame, |
| 126 std::unique_ptr<StringPairVector> headers) {} |
| 127 |
| 128 virtual void OnPing(std::unique_ptr<SpdyPingIR> frame) {} |
| 129 virtual void OnPingAck(std::unique_ptr<SpdyPingIR> frame); |
| 130 virtual void OnPriority(std::unique_ptr<SpdyPriorityIR> frame) {} |
| 131 |
| 132 // SpdyHeadersIR and SpdyPushPromiseIR each has a SpdyHeaderBlock which |
| 133 // significantly modifies the headers, so the actual header entries (name |
| 134 // and value strings) are provided in a vector. |
| 135 virtual void OnPushPromise(std::unique_ptr<SpdyPushPromiseIR> frame, |
| 136 std::unique_ptr<StringPairVector> headers) {} |
| 137 |
| 138 virtual void OnRstStream(std::unique_ptr<SpdyRstStreamIR> frame) {} |
| 139 |
| 140 // SpdySettingsIR has a map for settings, so loses info about the order of |
| 141 // settings, and whether the same setting appeared more than once, so the |
| 142 // the actual settings (parameter and value) are provided in a vector. |
| 143 virtual void OnSettings(std::unique_ptr<SpdySettingsIR> frame, |
| 144 std::unique_ptr<SettingVector> settings) {} |
| 145 |
| 146 // A settings frame with an ACK has no content, but for uniformity passing |
| 147 // a frame with the ACK flag set. |
| 148 virtual void OnSettingsAck(std::unique_ptr<SpdySettingsIR> frame); |
| 149 |
| 150 virtual void OnWindowUpdate(std::unique_ptr<SpdyWindowUpdateIR> frame) {} |
| 151 |
| 152 // The SpdyFramer will not process any more data at this point. |
| 153 virtual void OnError(SpdyFramer* framer, SpdyTestDeframer* deframer) {} |
| 154 }; |
| 155 |
| 156 class SpdyTestDeframer : public SpdyFramerVisitorInterface { |
| 157 public: |
| 158 ~SpdyTestDeframer() override {} |
| 159 |
| 160 // Creates a SpdyFramerVisitorInterface that builds SpdyFrameIR concrete |
| 161 // instances based on the callbacks it receives; when an entire frame is |
| 162 // decoded/reconstructed it calls the passed in SpdyDeframerVisitorInterface. |
| 163 // Transfers ownership of visitor to the new SpdyTestDeframer, which ensures |
| 164 // that it continues to exist while the SpdyTestDeframer exists. |
| 165 static std::unique_ptr<SpdyTestDeframer> CreateConverter( |
| 166 std::unique_ptr<SpdyDeframerVisitorInterface> visitor); |
| 167 |
| 168 // Call to notify the deframer that the SpdyFramer has returned after reaching |
| 169 // the end of decoding a frame. This is used to flush info about some frame |
| 170 // types where we don't get a clear end signal; others are flushed (i.e. the |
| 171 // appropriate call to the SpdyDeframerVisitorInterface method is invoked) |
| 172 // as they're decoded by SpdyFramer and it calls the deframer. See the |
| 173 // example in the comments at the top of this file. |
| 174 virtual bool AtFrameEnd() = 0; |
| 175 |
| 176 protected: |
| 177 SpdyTestDeframer() {} |
| 178 |
| 179 private: |
| 180 DISALLOW_COPY_AND_ASSIGN(SpdyTestDeframer); |
| 181 }; |
| 182 |
| 183 // CollectedFrame holds the result of one call to SpdyDeframerVisitorInterface, |
| 184 // as recorded by DeframerCallbackCollector. |
| 185 struct CollectedFrame { |
| 186 CollectedFrame(); |
| 187 CollectedFrame(CollectedFrame&& other); |
| 188 ~CollectedFrame(); |
| 189 CollectedFrame& operator=(CollectedFrame&& other); |
| 190 |
| 191 // Compare a SpdyFrameIR sub-class instance, expected_ir, against the |
| 192 // collected SpdyFrameIR. |
| 193 template <class T, |
| 194 typename X = |
| 195 typename std::enable_if<std::is_base_of<SpdyFrameIR, T>::value>> |
| 196 ::testing::AssertionResult VerifyHasFrame(const T& expected_ir) const { |
| 197 return VerifySpdyFrameIREquals(expected_ir, frame_ir.get()) |
| 198 ? ::testing::AssertionSuccess() |
| 199 : ::testing::AssertionFailure(); |
| 200 } |
| 201 |
| 202 // Compare the collected headers against a StringPairVector. Ignores |
| 203 // this->frame_ir. |
| 204 ::testing::AssertionResult VerifyHasHeaders( |
| 205 const StringPairVector& expected_headers) const; |
| 206 |
| 207 // Compare the collected settings (parameter and value pairs) against |
| 208 // expected_settings. Ignores this->frame_ir. |
| 209 ::testing::AssertionResult VerifyHasSettings( |
| 210 const SettingVector& expected_settings) const; |
| 211 |
| 212 std::unique_ptr<SpdyFrameIR> frame_ir; |
| 213 std::unique_ptr<StringPairVector> headers; |
| 214 std::unique_ptr<SettingVector> settings; |
| 215 bool error_reported = false; |
| 216 }; |
| 217 |
| 218 // Creates a CollectedFrame instance for each callback, storing it in the |
| 219 // vector provided to the constructor. |
| 220 class DeframerCallbackCollector : public SpdyDeframerVisitorInterface { |
| 221 public: |
| 222 explicit DeframerCallbackCollector( |
| 223 std::vector<CollectedFrame>* collected_frames); |
| 224 ~DeframerCallbackCollector() override {} |
| 225 |
| 226 void OnAltSvc(std::unique_ptr<SpdyAltSvcIR> frame_ir) override; |
| 227 void OnData(std::unique_ptr<SpdyDataIR> frame_ir) override; |
| 228 void OnGoAway(std::unique_ptr<SpdyGoAwayIR> frame_ir) override; |
| 229 void OnHeaders(std::unique_ptr<SpdyHeadersIR> frame_ir, |
| 230 std::unique_ptr<StringPairVector> headers) override; |
| 231 void OnPing(std::unique_ptr<SpdyPingIR> frame_ir) override; |
| 232 void OnPingAck(std::unique_ptr<SpdyPingIR> frame_ir) override; |
| 233 void OnPriority(std::unique_ptr<SpdyPriorityIR> frame_ir) override; |
| 234 void OnPushPromise(std::unique_ptr<SpdyPushPromiseIR> frame_ir, |
| 235 std::unique_ptr<StringPairVector> headers) override; |
| 236 void OnRstStream(std::unique_ptr<SpdyRstStreamIR> frame_ir) override; |
| 237 void OnSettings(std::unique_ptr<SpdySettingsIR> frame_ir, |
| 238 std::unique_ptr<SettingVector> settings) override; |
| 239 void OnSettingsAck(std::unique_ptr<SpdySettingsIR> frame_ir) override; |
| 240 void OnWindowUpdate(std::unique_ptr<SpdyWindowUpdateIR> frame_ir) override; |
| 241 void OnError(SpdyFramer* framer, SpdyTestDeframer* deframer) override; |
| 242 |
| 243 private: |
| 244 std::vector<CollectedFrame>* collected_frames_; |
| 245 }; |
| 246 |
| 247 } // namespace test |
| 248 } // namespace net |
| 249 |
| 250 #endif // NET_SPDY_SPDY_DEFRAMER_VISITOR_H_ |
OLD | NEW |