xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/http2/adapter/test_frame_sequence.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 #include "quiche/http2/adapter/test_frame_sequence.h"
2 
3 #include <memory>
4 #include <optional>
5 
6 #include "quiche/http2/adapter/http2_util.h"
7 #include "quiche/http2/adapter/oghttp2_util.h"
8 #include "quiche/spdy/core/hpack/hpack_encoder.h"
9 #include "quiche/spdy/core/spdy_framer.h"
10 
11 namespace http2 {
12 namespace adapter {
13 namespace test {
14 
ToHeaders(absl::Span<const std::pair<absl::string_view,absl::string_view>> headers)15 std::vector<Header> ToHeaders(
16     absl::Span<const std::pair<absl::string_view, absl::string_view>> headers) {
17   std::vector<Header> out;
18   for (auto [name, value] : headers) {
19     out.push_back(std::make_pair(HeaderRep(name), HeaderRep(value)));
20   }
21   return out;
22 }
23 
ClientPreface(absl::Span<const Http2Setting> settings)24 TestFrameSequence& TestFrameSequence::ClientPreface(
25     absl::Span<const Http2Setting> settings) {
26   preface_ = spdy::kHttp2ConnectionHeaderPrefix;
27   return Settings(settings);
28 }
29 
ServerPreface(absl::Span<const Http2Setting> settings)30 TestFrameSequence& TestFrameSequence::ServerPreface(
31     absl::Span<const Http2Setting> settings) {
32   return Settings(settings);
33 }
34 
Data(Http2StreamId stream_id,absl::string_view payload,bool fin,std::optional<int> padding_length)35 TestFrameSequence& TestFrameSequence::Data(Http2StreamId stream_id,
36                                            absl::string_view payload, bool fin,
37                                            std::optional<int> padding_length) {
38   auto data = std::make_unique<spdy::SpdyDataIR>(stream_id, payload);
39   data->set_fin(fin);
40   if (padding_length) {
41     data->set_padding_len(padding_length.value());
42   }
43   frames_.push_back(std::move(data));
44   return *this;
45 }
46 
RstStream(Http2StreamId stream_id,Http2ErrorCode error)47 TestFrameSequence& TestFrameSequence::RstStream(Http2StreamId stream_id,
48                                                 Http2ErrorCode error) {
49   frames_.push_back(std::make_unique<spdy::SpdyRstStreamIR>(
50       stream_id, TranslateErrorCode(error)));
51   return *this;
52 }
53 
Settings(absl::Span<const Http2Setting> settings)54 TestFrameSequence& TestFrameSequence::Settings(
55     absl::Span<const Http2Setting> settings) {
56   auto settings_frame = std::make_unique<spdy::SpdySettingsIR>();
57   for (const Http2Setting& setting : settings) {
58     settings_frame->AddSetting(setting.id, setting.value);
59   }
60   frames_.push_back(std::move(settings_frame));
61   return *this;
62 }
63 
SettingsAck()64 TestFrameSequence& TestFrameSequence::SettingsAck() {
65   auto settings = std::make_unique<spdy::SpdySettingsIR>();
66   settings->set_is_ack(true);
67   frames_.push_back(std::move(settings));
68   return *this;
69 }
70 
PushPromise(Http2StreamId stream_id,Http2StreamId promised_stream_id,absl::Span<const Header> headers)71 TestFrameSequence& TestFrameSequence::PushPromise(
72     Http2StreamId stream_id, Http2StreamId promised_stream_id,
73     absl::Span<const Header> headers) {
74   frames_.push_back(std::make_unique<spdy::SpdyPushPromiseIR>(
75       stream_id, promised_stream_id, ToHeaderBlock(headers)));
76   return *this;
77 }
78 
Ping(Http2PingId id)79 TestFrameSequence& TestFrameSequence::Ping(Http2PingId id) {
80   frames_.push_back(std::make_unique<spdy::SpdyPingIR>(id));
81   return *this;
82 }
83 
PingAck(Http2PingId id)84 TestFrameSequence& TestFrameSequence::PingAck(Http2PingId id) {
85   auto ping = std::make_unique<spdy::SpdyPingIR>(id);
86   ping->set_is_ack(true);
87   frames_.push_back(std::move(ping));
88   return *this;
89 }
90 
GoAway(Http2StreamId last_good_stream_id,Http2ErrorCode error,absl::string_view payload)91 TestFrameSequence& TestFrameSequence::GoAway(Http2StreamId last_good_stream_id,
92                                              Http2ErrorCode error,
93                                              absl::string_view payload) {
94   frames_.push_back(std::make_unique<spdy::SpdyGoAwayIR>(
95       last_good_stream_id, TranslateErrorCode(error), std::string(payload)));
96   return *this;
97 }
98 
Headers(Http2StreamId stream_id,absl::Span<const std::pair<absl::string_view,absl::string_view>> headers,bool fin,bool add_continuation)99 TestFrameSequence& TestFrameSequence::Headers(
100     Http2StreamId stream_id,
101     absl::Span<const std::pair<absl::string_view, absl::string_view>> headers,
102     bool fin, bool add_continuation) {
103   return Headers(stream_id, ToHeaders(headers), fin, add_continuation);
104 }
105 
Headers(Http2StreamId stream_id,spdy::Http2HeaderBlock block,bool fin,bool add_continuation)106 TestFrameSequence& TestFrameSequence::Headers(Http2StreamId stream_id,
107                                               spdy::Http2HeaderBlock block,
108                                               bool fin, bool add_continuation) {
109   if (add_continuation) {
110     // The normal intermediate representations don't allow you to represent a
111     // nonterminal HEADERS frame explicitly, so we'll need to use
112     // SpdyUnknownIRs. For simplicity, and in order not to mess up HPACK state,
113     // the payload will be uncompressed.
114     spdy::HpackEncoder encoder;
115     encoder.DisableCompression();
116     std::string encoded_block = encoder.EncodeHeaderBlock(block);
117     const size_t pos = encoded_block.size() / 2;
118     const uint8_t flags = fin ? END_STREAM_FLAG : 0x0;
119     frames_.push_back(std::make_unique<spdy::SpdyUnknownIR>(
120         stream_id, static_cast<uint8_t>(spdy::SpdyFrameType::HEADERS), flags,
121         encoded_block.substr(0, pos)));
122 
123     auto continuation = std::make_unique<spdy::SpdyContinuationIR>(stream_id);
124     continuation->set_end_headers(true);
125     continuation->take_encoding(encoded_block.substr(pos));
126     frames_.push_back(std::move(continuation));
127   } else {
128     auto headers =
129         std::make_unique<spdy::SpdyHeadersIR>(stream_id, std::move(block));
130     headers->set_fin(fin);
131     frames_.push_back(std::move(headers));
132   }
133   return *this;
134 }
135 
Headers(Http2StreamId stream_id,absl::Span<const Header> headers,bool fin,bool add_continuation)136 TestFrameSequence& TestFrameSequence::Headers(Http2StreamId stream_id,
137                                               absl::Span<const Header> headers,
138                                               bool fin, bool add_continuation) {
139   return Headers(stream_id, ToHeaderBlock(headers), fin, add_continuation);
140 }
141 
WindowUpdate(Http2StreamId stream_id,int32_t delta)142 TestFrameSequence& TestFrameSequence::WindowUpdate(Http2StreamId stream_id,
143                                                    int32_t delta) {
144   frames_.push_back(
145       std::make_unique<spdy::SpdyWindowUpdateIR>(stream_id, delta));
146   return *this;
147 }
148 
Priority(Http2StreamId stream_id,Http2StreamId parent_stream_id,int weight,bool exclusive)149 TestFrameSequence& TestFrameSequence::Priority(Http2StreamId stream_id,
150                                                Http2StreamId parent_stream_id,
151                                                int weight, bool exclusive) {
152   frames_.push_back(std::make_unique<spdy::SpdyPriorityIR>(
153       stream_id, parent_stream_id, weight, exclusive));
154   return *this;
155 }
156 
Metadata(Http2StreamId stream_id,absl::string_view payload,bool multiple_frames)157 TestFrameSequence& TestFrameSequence::Metadata(Http2StreamId stream_id,
158                                                absl::string_view payload,
159                                                bool multiple_frames) {
160   if (multiple_frames) {
161     const size_t pos = payload.size() / 2;
162     frames_.push_back(std::make_unique<spdy::SpdyUnknownIR>(
163         stream_id, kMetadataFrameType, 0, std::string(payload.substr(0, pos))));
164     frames_.push_back(std::make_unique<spdy::SpdyUnknownIR>(
165         stream_id, kMetadataFrameType, kMetadataEndFlag,
166         std::string(payload.substr(pos))));
167   } else {
168     frames_.push_back(std::make_unique<spdy::SpdyUnknownIR>(
169         stream_id, kMetadataFrameType, kMetadataEndFlag, std::string(payload)));
170   }
171   return *this;
172 }
173 
Serialize()174 std::string TestFrameSequence::Serialize() {
175   std::string result;
176   if (!preface_.empty()) {
177     result = preface_;
178   }
179   spdy::SpdyFramer framer(spdy::SpdyFramer::ENABLE_COMPRESSION);
180   for (const auto& frame : frames_) {
181     spdy::SpdySerializedFrame f = framer.SerializeFrame(*frame);
182     absl::StrAppend(&result, absl::string_view(f));
183   }
184   return result;
185 }
186 
187 }  // namespace test
188 }  // namespace adapter
189 }  // namespace http2
190