/proc/self/cwd/test/integration/h2_fuzz.cc
Line | Count | Source (jump to first uncovered line) |
1 | | #include "test/integration/h2_fuzz.h" |
2 | | |
3 | | #include <functional> |
4 | | |
5 | | #include "source/common/common/assert.h" |
6 | | #include "source/common/common/base64.h" |
7 | | #include "source/common/common/logger.h" |
8 | | |
9 | | #include "test/test_common/environment.h" |
10 | | |
11 | | namespace Envoy { |
12 | | |
13 | | using namespace Envoy::Http::Http2; |
14 | | |
15 | | namespace { |
16 | | |
17 | | static Http2Frame::HeadersFlags |
18 | 179 | unifyHeadersFlags(const Protobuf::RepeatedField<int>& headers_flags) { |
19 | 179 | int unified_flags = 0; |
20 | 179 | for (const auto& flag : headers_flags) { |
21 | 122 | unified_flags |= flag; |
22 | 122 | } |
23 | 179 | return static_cast<Http2Frame::HeadersFlags>(unified_flags); |
24 | 179 | } |
25 | | |
26 | | } // namespace |
27 | | |
28 | | void H2FuzzIntegrationTest::sendFrame(const test::integration::H2TestFrame& proto_frame, |
29 | 7.07k | std::function<void(const Http2Frame&)> write_func) { |
30 | 7.07k | Http2Frame h2_frame; |
31 | 7.07k | switch (proto_frame.frame_type_case()) { |
32 | 341 | case test::integration::H2TestFrame::kPing: |
33 | 341 | ENVOY_LOG_MISC(trace, "Sending ping frame"); |
34 | 341 | h2_frame = Http2Frame::makePingFrame(proto_frame.ping().data()); |
35 | 341 | break; |
36 | 826 | case test::integration::H2TestFrame::kSettings: { |
37 | 826 | const Http2Frame::SettingsFlags settings_flags = |
38 | 826 | static_cast<Http2Frame::SettingsFlags>(proto_frame.settings().flags()); |
39 | 826 | ENVOY_LOG_MISC(trace, "Sending settings frame"); |
40 | 826 | h2_frame = Http2Frame::makeEmptySettingsFrame(settings_flags); |
41 | 826 | break; |
42 | 0 | } |
43 | 68 | case test::integration::H2TestFrame::kHeaders: { |
44 | 68 | const Http2Frame::HeadersFlags headers_flags = unifyHeadersFlags(proto_frame.headers().flags()); |
45 | 68 | const uint32_t stream_idx = proto_frame.headers().stream_index(); |
46 | 68 | ENVOY_LOG_MISC(trace, "Sending headers frame"); |
47 | 68 | h2_frame = Http2Frame::makeEmptyHeadersFrame(stream_idx, headers_flags); |
48 | 68 | break; |
49 | 0 | } |
50 | 76 | case test::integration::H2TestFrame::kContinuation: { |
51 | 76 | const Http2Frame::HeadersFlags headers_flags = |
52 | 76 | unifyHeadersFlags(proto_frame.continuation().flags()); |
53 | 76 | const uint32_t stream_idx = proto_frame.continuation().stream_index(); |
54 | 76 | ENVOY_LOG_MISC(trace, "Sending continuation frame"); |
55 | 76 | h2_frame = Http2Frame::makeEmptyContinuationFrame(stream_idx, headers_flags); |
56 | 76 | break; |
57 | 0 | } |
58 | 176 | case test::integration::H2TestFrame::kData: { |
59 | 176 | const Http2Frame::DataFlags data_flags = |
60 | 176 | static_cast<Http2Frame::DataFlags>(proto_frame.data().flags()); |
61 | 176 | const uint32_t stream_idx = proto_frame.data().stream_index(); |
62 | 176 | ENVOY_LOG_MISC(trace, "Sending data frame"); |
63 | 176 | h2_frame = Http2Frame::makeEmptyDataFrame(stream_idx, data_flags); |
64 | 176 | break; |
65 | 0 | } |
66 | 280 | case test::integration::H2TestFrame::kPriority: { |
67 | 280 | const uint32_t stream_idx = proto_frame.priority().stream_index(); |
68 | 280 | const uint32_t dependent_idx = proto_frame.priority().dependent_index(); |
69 | 280 | ENVOY_LOG_MISC(trace, "Sending priority frame"); |
70 | 280 | h2_frame = Http2Frame::makePriorityFrame(stream_idx, dependent_idx); |
71 | 280 | break; |
72 | 0 | } |
73 | 35 | case test::integration::H2TestFrame::kPushPromise: { |
74 | 35 | const Http2Frame::HeadersFlags headers_flags = |
75 | 35 | unifyHeadersFlags(proto_frame.push_promise().flags()); |
76 | 35 | const uint32_t stream_idx = proto_frame.push_promise().stream_index(); |
77 | 35 | const uint32_t promised_stream_idx = proto_frame.push_promise().promised_stream_index(); |
78 | 35 | ENVOY_LOG_MISC(trace, "Sending push promise frame"); |
79 | 35 | h2_frame = |
80 | 35 | Http2Frame::makeEmptyPushPromiseFrame(stream_idx, promised_stream_idx, headers_flags); |
81 | 35 | break; |
82 | 0 | } |
83 | 79 | case test::integration::H2TestFrame::kResetStream: { |
84 | 79 | const uint32_t stream_idx = proto_frame.reset_stream().stream_index(); |
85 | 79 | const Http2Frame::ErrorCode error_code = |
86 | 79 | static_cast<Http2Frame::ErrorCode>(proto_frame.reset_stream().error_code()); |
87 | 79 | ENVOY_LOG_MISC(trace, "Sending reset stream frame"); |
88 | 79 | h2_frame = Http2Frame::makeResetStreamFrame(stream_idx, error_code); |
89 | 79 | break; |
90 | 0 | } |
91 | 103 | case test::integration::H2TestFrame::kGoAway: { |
92 | 103 | const uint32_t last_stream_idx = proto_frame.go_away().last_stream_index(); |
93 | 103 | const Http2Frame::ErrorCode error_code = |
94 | 103 | static_cast<Http2Frame::ErrorCode>(proto_frame.go_away().error_code()); |
95 | 103 | ENVOY_LOG_MISC(trace, "Sending go-away frame"); |
96 | 103 | h2_frame = Http2Frame::makeEmptyGoAwayFrame(last_stream_idx, error_code); |
97 | 103 | break; |
98 | 0 | } |
99 | 135 | case test::integration::H2TestFrame::kWindowUpdate: { |
100 | 135 | const uint32_t stream_idx = proto_frame.window_update().stream_index(); |
101 | 135 | const uint32_t increment = proto_frame.window_update().increment(); |
102 | 135 | ENVOY_LOG_MISC(trace, "Sending windows_update frame"); |
103 | 135 | h2_frame = Http2Frame::makeWindowUpdateFrame(stream_idx, increment); |
104 | 135 | break; |
105 | 0 | } |
106 | 45 | case test::integration::H2TestFrame::kMalformedRequest: { |
107 | 45 | const uint32_t stream_idx = proto_frame.malformed_request().stream_index(); |
108 | 45 | ENVOY_LOG_MISC(trace, "Sending malformed_request frame"); |
109 | 45 | h2_frame = Http2Frame::makeMalformedRequest(stream_idx); |
110 | 45 | break; |
111 | 0 | } |
112 | 34 | case test::integration::H2TestFrame::kMalformedRequestWithZerolenHeader: { |
113 | 34 | const uint32_t stream_idx = proto_frame.malformed_request_with_zerolen_header().stream_index(); |
114 | 34 | const absl::string_view host = proto_frame.malformed_request_with_zerolen_header().host(); |
115 | 34 | const absl::string_view path = proto_frame.malformed_request_with_zerolen_header().path(); |
116 | 34 | ENVOY_LOG_MISC(trace, "Sending malformed_request_with_zerolen_header"); |
117 | 34 | h2_frame = Http2Frame::makeMalformedRequestWithZerolenHeader(stream_idx, host, path); |
118 | 34 | break; |
119 | 0 | } |
120 | 244 | case test::integration::H2TestFrame::kRequest: { |
121 | 244 | const uint32_t stream_idx = proto_frame.request().stream_index(); |
122 | 244 | const absl::string_view host = proto_frame.request().host(); |
123 | 244 | const absl::string_view path = proto_frame.request().path(); |
124 | 244 | ENVOY_LOG_MISC(trace, "Sending request"); |
125 | 244 | h2_frame = Http2Frame::makeRequest(stream_idx, host, path); |
126 | 244 | break; |
127 | 0 | } |
128 | 170 | case test::integration::H2TestFrame::kPostRequest: { |
129 | 170 | const uint32_t stream_idx = proto_frame.post_request().stream_index(); |
130 | 170 | const absl::string_view host = proto_frame.post_request().host(); |
131 | 170 | const absl::string_view path = proto_frame.post_request().path(); |
132 | 170 | ENVOY_LOG_MISC(trace, "Sending post request"); |
133 | 170 | h2_frame = Http2Frame::makePostRequest(stream_idx, host, path); |
134 | 170 | break; |
135 | 0 | } |
136 | 2.25k | case test::integration::H2TestFrame::kMetadata: { |
137 | 2.25k | const Http2Frame::MetadataFlags metadata_flags = |
138 | 2.25k | static_cast<Http2Frame::MetadataFlags>(proto_frame.metadata().flags()); |
139 | 2.25k | const uint32_t stream_idx = proto_frame.metadata().stream_index(); |
140 | 2.25k | Http::MetadataMap metadata_map; |
141 | 8.06k | for (const auto& metadataPair : proto_frame.metadata().metadata().metadata()) { |
142 | 8.06k | metadata_map.insert(metadataPair); |
143 | 8.06k | } |
144 | 2.25k | ENVOY_LOG_MISC(trace, "Sending metadata frame."); |
145 | 2.25k | h2_frame = |
146 | 2.25k | Http2Frame::makeMetadataFrameFromMetadataMap(stream_idx, metadata_map, metadata_flags); |
147 | 2.25k | break; |
148 | 0 | } |
149 | 58 | case test::integration::H2TestFrame::kStatus: { |
150 | 58 | const std::string status = proto_frame.status().status(); |
151 | 58 | const uint32_t stream_idx = proto_frame.status().stream_index(); |
152 | 58 | ENVOY_LOG_MISC(trace, "Sending status frame"); |
153 | 58 | h2_frame = Http2Frame::makeHeadersFrameWithStatus(status, stream_idx); |
154 | 58 | break; |
155 | 0 | } |
156 | 391 | case test::integration::H2TestFrame::kGeneric: { |
157 | 391 | const absl::string_view frame_bytes = proto_frame.generic().frame_bytes(); |
158 | 391 | ENVOY_LOG_MISC(trace, "Sending generic frame"); |
159 | 391 | h2_frame = Http2Frame::makeGenericFrame(frame_bytes); |
160 | 391 | break; |
161 | 0 | } |
162 | 1.75k | default: |
163 | 1.75k | ENVOY_LOG_MISC(debug, "Proto-frame not supported!"); |
164 | 1.75k | break; |
165 | 7.07k | } |
166 | | |
167 | 7.07k | write_func(h2_frame); |
168 | 7.07k | } |
169 | | |
170 | | void H2FuzzIntegrationTest::replay(const test::integration::H2CaptureFuzzTestCase& input, |
171 | 1.04k | bool ignore_response) { |
172 | 1.04k | struct Init { |
173 | 1.04k | Init(H2FuzzIntegrationTest* test) { test->initialize(); } |
174 | 1.04k | }; |
175 | 1.04k | PERSISTENT_FUZZ_VAR(Init, initialized, (this)); |
176 | 1.04k | UNREFERENCED_PARAMETER(initialized); |
177 | 1.04k | IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("http")); |
178 | 1.04k | FakeRawConnectionPtr fake_upstream_connection; |
179 | 1.04k | bool stop_further_inputs = false; |
180 | 1.04k | bool preamble_sent = false; |
181 | 10.5k | for (int i = 0; i < input.events().size(); ++i) { |
182 | 9.57k | if (stop_further_inputs) { |
183 | 109 | break; |
184 | 109 | } |
185 | 9.46k | const auto& event = input.events(i); |
186 | 9.46k | ENVOY_LOG_MISC(debug, "Processing event: {}", event.DebugString()); |
187 | | // If we're disconnected, we fail out. |
188 | 9.46k | if (!tcp_client->connected()) { |
189 | 2 | ENVOY_LOG_MISC(debug, "Disconnected, no further event processing."); |
190 | 2 | break; |
191 | 2 | } |
192 | 9.46k | switch (event.event_selector_case()) { |
193 | 786 | case test::integration::Event::kDownstreamSendEvent: { |
194 | 3.54k | auto downstream_write_func = [&](const Http2Frame& h2_frame) -> void { |
195 | 3.54k | ASSERT_TRUE(tcp_client->write(std::string(h2_frame), false, false)); |
196 | 3.54k | }; |
197 | 786 | if (!preamble_sent) { |
198 | | // Start H2 session - send hello string |
199 | 315 | ASSERT_TRUE(tcp_client->write(Http2Frame::Preamble, false, false)); |
200 | 315 | preamble_sent = true; |
201 | 315 | } |
202 | 3.55k | for (auto& frame : event.downstream_send_event().h2_frames()) { |
203 | 3.55k | if (!tcp_client->connected()) { |
204 | 4 | ENVOY_LOG_MISC(debug, |
205 | 4 | "Disconnected, avoiding sending data, no further event processing."); |
206 | 4 | break; |
207 | 4 | } |
208 | | |
209 | 3.54k | ENVOY_LOG_MISC(trace, "sending downstream frame"); |
210 | 3.54k | sendFrame(frame, downstream_write_func); |
211 | 3.54k | } |
212 | 786 | break; |
213 | 786 | } |
214 | 797 | case test::integration::Event::kUpstreamSendEvent: { |
215 | 797 | if (ignore_response) { |
216 | 346 | break; |
217 | 346 | } |
218 | 451 | if (fake_upstream_connection == nullptr) { |
219 | 197 | if (!fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection, max_wait_ms_)) { |
220 | | // If we timed out, we fail out. |
221 | 139 | if (tcp_client->connected()) { |
222 | 139 | tcp_client->close(); |
223 | 139 | } |
224 | 139 | stop_further_inputs = true; |
225 | 139 | break; |
226 | 139 | } |
227 | 197 | } |
228 | | // If we're no longer connected, we're done. |
229 | 312 | if (!fake_upstream_connection->connected()) { |
230 | 6 | if (tcp_client->connected()) { |
231 | 6 | tcp_client->close(); |
232 | 6 | } |
233 | 6 | stop_further_inputs = true; |
234 | 6 | break; |
235 | 6 | } |
236 | 306 | { |
237 | 3.52k | auto upstream_write_func = [&](const Http2Frame& h2_frame) -> void { |
238 | 3.52k | AssertionResult result = fake_upstream_connection->write(std::string(h2_frame)); |
239 | 3.52k | RELEASE_ASSERT(result, result.message()); |
240 | 3.52k | }; |
241 | 3.54k | for (auto& frame : event.upstream_send_event().h2_frames()) { |
242 | 3.54k | if (!fake_upstream_connection->connected()) { |
243 | 25 | ENVOY_LOG_MISC( |
244 | 25 | debug, |
245 | 25 | "Upstream disconnected, avoiding sending data, no further event processing."); |
246 | 25 | stop_further_inputs = true; |
247 | 25 | break; |
248 | 25 | } |
249 | | |
250 | 3.52k | ENVOY_LOG_MISC(trace, "sending upstream frame"); |
251 | 3.52k | sendFrame(frame, upstream_write_func); |
252 | 3.52k | } |
253 | 306 | } |
254 | 306 | break; |
255 | 312 | } |
256 | 7.87k | default: |
257 | | // Maybe nothing is set? |
258 | 7.87k | break; |
259 | 9.46k | } |
260 | 9.46k | } |
261 | 1.04k | if (fake_upstream_connection != nullptr) { |
262 | 58 | if (fake_upstream_connection->connected()) { |
263 | 20 | AssertionResult result = fake_upstream_connection->close(); |
264 | 20 | RELEASE_ASSERT(result, result.message()); |
265 | 20 | } |
266 | 58 | AssertionResult result = fake_upstream_connection->waitForDisconnect(); |
267 | 58 | RELEASE_ASSERT(result, result.message()); |
268 | 58 | } |
269 | 1.04k | if (tcp_client->connected()) { |
270 | 1.03k | tcp_client->close(); |
271 | 1.03k | } |
272 | 1.04k | } |
273 | | |
274 | | } // namespace Envoy |