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 // SpdyStringPiece 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 <type_traits> | |
74 #include <utility> | |
75 #include <vector> | |
76 | |
77 #include "base/logging.h" | |
78 #include "base/macros.h" | |
79 #include "net/spdy/platform/api/spdy_string.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<SpdyString, SpdyString> 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 |