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