1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
|
#ifndef AWS_HTTP_H2_TEST_HELPER_H
#define AWS_HTTP_H2_TEST_HELPER_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/common/array_list.h>
#include <aws/http/private/h2_frames.h>
#include <aws/http/request_response.h>
#include <aws/testing/aws_test_harness.h>
struct aws_input_stream;
#define ASSERT_H2ERR_SUCCESS(condition, ...) \
do { \
struct aws_h2err assert_rv = (condition); \
if (!aws_h2err_success(assert_rv)) { \
if (!PRINT_FAIL_INTERNAL0(__VA_ARGS__)) { \
PRINT_FAIL_INTERNAL0( \
"Expected success at %s; got aws_h2err{%s, %s}\n", \
#condition, \
aws_http2_error_code_to_str(assert_rv.h2_code), \
aws_error_name(assert_rv.aws_code)); \
} \
POSTFAIL_INTERNAL(); \
} \
} while (0)
#define ASSERT_H2ERR_FAILS(condition, ...) \
do { \
struct aws_h2err assert_rv = (condition); \
if (!aws_h2err_failed(assert_rv)) { \
if (!PRINT_FAIL_INTERNAL0(__VA_ARGS__)) { \
PRINT_FAIL_INTERNAL0("Expected failure at %s; got AWS_H2ERR_SUCCESS\n", #condition); \
} \
POSTFAIL_INTERNAL(); \
} \
} while (0)
#define ASSERT_H2ERR_ERROR(h2_error, condition, ...) \
do { \
struct aws_h2err assert_rv = (condition); \
if (!aws_h2err_failed(assert_rv)) { \
if (!PRINT_FAIL_INTERNAL0(__VA_ARGS__)) { \
PRINT_FAIL_INTERNAL0( \
"Expected %s failure at %s; got AWS_H2ERR_SUCCESS\n", \
aws_http2_error_code_to_str(h2_error), \
#condition); \
} \
POSTFAIL_INTERNAL(); \
} \
if (assert_rv.h2_code != h2_error) { \
PRINT_FAIL_INTERNAL0( \
"Expected %s failure at %s; got aws_h2err{%s, %s}\n", \
aws_http2_error_code_to_str(h2_error), \
#condition, \
aws_http2_error_code_to_str(assert_rv.h2_code), \
aws_error_name(assert_rv.aws_code)); \
} \
} while (0)
/**
* Information gathered about a given frame from decoder callbacks.
* These aren't 1:1 with literal H2 frames:
* - The decoder hides the existence of CONTINUATION frames,
* their data continues the preceding HEADERS or PUSH_PROMISE frame.
*
* - A DATA frame could appear as N on_data callbacks.
*
* - The on_end_stream callback fires after all other callbacks for that frame,
* so we count it as part of the preceding "finished" frame.
*/
struct h2_decoded_frame {
/* If true, we expect no further callbacks regarding this frame */
bool finished;
enum aws_h2_frame_type type; /* All frame types have this */
uint32_t stream_id; /* All frame types have this */
/*
* Everything else is only found in certain frame types
*/
bool end_stream; /* HEADERS and DATA might have this */
bool ack; /* PING and SETTINGS might have this */
uint32_t error_code; /* RST_STREAM and GOAWAY have this */
uint32_t promised_stream_id; /* PUSH_PROMISE has this */
uint32_t goaway_last_stream_id; /* GOAWAY has this */
uint8_t ping_opaque_data[AWS_HTTP2_PING_DATA_SIZE]; /* PING has this */
uint32_t window_size_increment; /* WINDOW_UPDATE has this */
struct aws_http_headers *headers; /* HEADERS and PUSH_PROMISE have this */
bool headers_malformed; /* HEADERS and PUSH_PROMISE have this */
enum aws_http_header_block header_block_type; /* HEADERS have this */
struct aws_array_list settings; /* contains aws_http2_setting, SETTINGS has this */
struct aws_byte_buf data; /* DATA and GOAWAY have this */
uint32_t data_payload_len; /* DATA has this */
bool data_end_stream; /* DATA has this */
};
/**
* Check that:
* - frame finished (ex: if HEADERS frame, then on_headers_end() fired)
* - frame was in fact using the expected type and stream_id.
*/
int h2_decoded_frame_check_finished(
const struct h2_decoded_frame *frame,
enum aws_h2_frame_type expected_type,
uint32_t expected_stream_id);
/******************************************************************************/
/**
* Translates decoder callbacks into an array-list of h2_decoded_frames.
*/
struct h2_decode_tester {
struct aws_allocator *alloc;
struct aws_h2_decoder *decoder;
struct aws_array_list frames; /* contains h2_decoded_frame */
};
struct h2_decode_tester_options {
struct aws_allocator *alloc;
bool is_server;
bool skip_connection_preface;
};
int h2_decode_tester_init(struct h2_decode_tester *decode_tester, const struct h2_decode_tester_options *options);
void h2_decode_tester_clean_up(struct h2_decode_tester *decode_tester);
size_t h2_decode_tester_frame_count(const struct h2_decode_tester *decode_tester);
struct h2_decoded_frame *h2_decode_tester_get_frame(const struct h2_decode_tester *decode_tester, size_t i);
struct h2_decoded_frame *h2_decode_tester_latest_frame(const struct h2_decode_tester *decode_tester);
/**
* Search for frame of a given type, starting at specified index.
* To search for the next frame, pass search_start_idx = prev_idx + 1
*/
struct h2_decoded_frame *h2_decode_tester_find_frame(
const struct h2_decode_tester *decode_tester,
enum aws_h2_frame_type type,
size_t search_start_idx,
size_t *out_idx);
/**
* Search for frame of a given stream-id, starting at specified index.
* To search for the next frame, pass search_start_idx = prev_idx + 1
*/
struct h2_decoded_frame *h2_decode_tester_find_stream_frame_any_type(
const struct h2_decode_tester *decode_tester,
uint32_t stream_id,
size_t search_start_idx,
size_t *out_idx);
/**
* Search for frame of a given type and stream-id, starting at specified index.
* To search for the next frame, pass search_start_idx = prev_idx + 1
*/
struct h2_decoded_frame *h2_decode_tester_find_stream_frame(
const struct h2_decode_tester *decode_tester,
enum aws_h2_frame_type type,
uint32_t stream_id,
size_t search_start_idx,
size_t *out_idx);
/**
* Compare data (which may be split across N frames) against expected
*/
int h2_decode_tester_check_data_across_frames(
const struct h2_decode_tester *decode_tester,
uint32_t stream_id,
struct aws_byte_cursor expected,
bool expect_end_stream);
/**
* Compare data (which may be split across N frames) against expected
*/
int h2_decode_tester_check_data_str_across_frames(
const struct h2_decode_tester *decode_tester,
uint32_t stream_id,
const char *expected,
bool expect_end_stream);
/******************************************************************************/
/**
* Fake HTTP/2 peer.
* Can decode H2 frames that are are written to the testing channel.
* Can encode H2 frames and push it into the channel in the read direction.
*/
struct h2_fake_peer {
struct aws_allocator *alloc;
struct testing_channel *testing_channel;
struct aws_h2_frame_encoder encoder;
struct h2_decode_tester decode;
bool is_server;
};
struct h2_fake_peer_options {
struct aws_allocator *alloc;
struct testing_channel *testing_channel;
bool is_server;
};
int h2_fake_peer_init(struct h2_fake_peer *peer, const struct h2_fake_peer_options *options);
void h2_fake_peer_clean_up(struct h2_fake_peer *peer);
/**
* Pop all written messages off the testing-channel and run them through the peer's decode-tester
*/
int h2_fake_peer_decode_messages_from_testing_channel(struct h2_fake_peer *peer);
/**
* Encode frame and push it into the testing-channel in the read-direction.
* Takes ownership of frame and destroys after sending.
*/
int h2_fake_peer_send_frame(struct h2_fake_peer *peer, struct aws_h2_frame *frame);
/**
* Encode the entire byte cursor into a single DATA frame.
* Fails if the cursor is too large for this to work.
*/
int h2_fake_peer_send_data_frame(
struct h2_fake_peer *peer,
uint32_t stream_id,
struct aws_byte_cursor data,
bool end_stream);
/**
* Encode the entire byte cursor into a single DATA frame.
* Fails if the cursor is too large for this to work.
*/
int h2_fake_peer_send_data_frame_with_padding_length(
struct h2_fake_peer *peer,
uint32_t stream_id,
struct aws_byte_cursor data,
bool end_stream,
uint8_t padding_length);
/**
* Encode the entire string into a single DATA frame.
* Fails if the string is too large for this to work.
*/
int h2_fake_peer_send_data_frame_str(struct h2_fake_peer *peer, uint32_t stream_id, const char *data, bool end_stream);
/**
* Peer sends the connection preface with specified settings.
* Takes ownership of frame and destroys after sending
*/
int h2_fake_peer_send_connection_preface(struct h2_fake_peer *peer, struct aws_h2_frame *settings);
/**
* Peer sends the connection preface with default settings.
*/
int h2_fake_peer_send_connection_preface_default_settings(struct h2_fake_peer *peer);
/******************************************************************************/
/**
* Create input stream that can do weird stuff in tests
*/
struct aws_input_stream *aws_input_stream_new_tester(struct aws_allocator *alloc, struct aws_byte_cursor cursor);
void aws_input_stream_tester_set_max_bytes_per_read(struct aws_input_stream *input_stream, size_t max_bytes);
void aws_input_stream_tester_set_reading_broken(struct aws_input_stream *input_stream, bool is_broken);
/**
* Create input stream that can upload a certain length of stuff
*/
struct aws_input_stream *aws_input_stream_tester_upload_new(struct aws_allocator *alloc, size_t length);
size_t aws_input_stream_tester_upload_get_num_sentence_sent(struct aws_input_stream *stream);
#endif /* AWS_HTTP_H2_TEST_HELPER_H */
|