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