Skip to content

Commit 3c4527e

Browse files
authored
Merge branch 'awslabs:main' into main
2 parents d2d34ae + 6586c80 commit 3c4527e

File tree

12 files changed

+124
-44
lines changed

12 files changed

+124
-44
lines changed

include/aws/http/exports.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* SPDX-License-Identifier: Apache-2.0.
77
*/
88

9-
#if defined(USE_WINDOWS_DLL_SEMANTICS) || defined(WIN32)
9+
#if defined(AWS_CRT_USE_WINDOWS_DLL_SEMANTICS) || defined(_WIN32)
1010
# ifdef AWS_HTTP_USE_IMPORT_EXPORT
1111
# ifdef AWS_HTTP_EXPORTS
1212
# define AWS_HTTP_API __declspec(dllexport)
@@ -18,12 +18,12 @@
1818
# endif /* USE_IMPORT_EXPORT */
1919

2020
#else
21-
# if ((__GNUC__ >= 4) || defined(__clang__)) && defined(AWS_HTTP_USE_IMPORT_EXPORT) && defined(AWS_HTTP_EXPORTS)
21+
# if defined(AWS_HTTP_USE_IMPORT_EXPORT) && defined(AWS_HTTP_EXPORTS)
2222
# define AWS_HTTP_API __attribute__((visibility("default")))
2323
# else
2424
# define AWS_HTTP_API
25-
# endif /* __GNUC__ >= 4 || defined(__clang__) */
25+
# endif
2626

27-
#endif /* defined(USE_WINDOWS_DLL_SEMANTICS) || defined(WIN32) */
27+
#endif /* defined(AWS_CRT_USE_WINDOWS_DLL_SEMANTICS) || defined(_WIN32) */
2828

2929
#endif /* AWS_HTTP_EXPORTS_H */

include/aws/http/private/h2_frames.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ int aws_h2_encode_frame(
207207
* body_complete will be set true if encoder reaches the end of the body_stream.
208208
* body_stalled will be true if aws_input_stream_read() stopped early (didn't
209209
* complete, though more space was available).
210+
* body_failed will be true if the aws_input_stream was the cause of an error.
210211
*
211212
* Each call to this function encodes a complete DATA frame, or nothing at all,
212213
* so it's always safe to encode a different frame type or the body of a different stream
@@ -223,7 +224,8 @@ int aws_h2_encode_data_frame(
223224
size_t *connection_window_size_peer,
224225
struct aws_byte_buf *output,
225226
bool *body_complete,
226-
bool *body_stalled);
227+
bool *body_stalled,
228+
bool *body_failed);
227229

228230
AWS_HTTP_API
229231
void aws_h2_frame_destroy(struct aws_h2_frame *frame);

source/h2_frames.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,13 +321,15 @@ int aws_h2_encode_data_frame(
321321
size_t *connection_window_size_peer,
322322
struct aws_byte_buf *output,
323323
bool *body_complete,
324-
bool *body_stalled) {
324+
bool *body_stalled,
325+
bool *body_failed) {
325326

326327
AWS_PRECONDITION(encoder);
327328
AWS_PRECONDITION(body_stream);
328329
AWS_PRECONDITION(output);
329330
AWS_PRECONDITION(body_complete);
330331
AWS_PRECONDITION(body_stalled);
332+
AWS_PRECONDITION(body_failed);
331333
AWS_PRECONDITION(*stream_window_size_peer > 0);
332334

333335
if (aws_h2_validate_stream_id(stream_id)) {
@@ -336,6 +338,7 @@ int aws_h2_encode_data_frame(
336338

337339
*body_complete = false;
338340
*body_stalled = false;
341+
*body_failed = false;
339342
uint8_t flags = 0;
340343

341344
/*
@@ -378,12 +381,14 @@ int aws_h2_encode_data_frame(
378381

379382
/* Read body into sub-buffer */
380383
if (aws_input_stream_read(body_stream, &body_sub_buf)) {
384+
*body_failed = true;
381385
goto error;
382386
}
383387

384388
/* Check if we've reached the end of the body */
385389
struct aws_stream_status body_status;
386390
if (aws_input_stream_get_status(body_stream, &body_status)) {
391+
*body_failed = true;
387392
goto error;
388393
}
389394

source/h2_stream.c

Lines changed: 22 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ static void s_stream_data_write_destroy(
407407

408408
AWS_PRECONDITION(stream);
409409
AWS_PRECONDITION(write);
410+
AWS_PRECONDITION(!aws_linked_list_node_is_in_list(&stream->node));
410411
if (write->on_complete) {
411412
write->on_complete(&stream->base, error_code, write->user_data);
412413
}
@@ -682,37 +683,13 @@ static inline bool s_h2_stream_has_outgoing_writes(struct aws_h2_stream *stream)
682683
return !aws_linked_list_empty(&stream->thread_data.outgoing_writes);
683684
}
684685

685-
static void s_h2_stream_write_data_complete(struct aws_h2_stream *stream, bool *waiting_writes) {
686-
AWS_PRECONDITION(waiting_writes);
687-
AWS_PRECONDITION(s_h2_stream_has_outgoing_writes(stream));
688-
689-
/* finish/clean up the current write operation */
690-
struct aws_linked_list_node *node = aws_linked_list_pop_front(&stream->thread_data.outgoing_writes);
691-
struct aws_h2_stream_data_write *write_op = AWS_CONTAINER_OF(node, struct aws_h2_stream_data_write, node);
692-
const bool ending_stream = write_op->end_stream;
693-
s_stream_data_write_destroy(stream, write_op, AWS_OP_SUCCESS);
694-
695-
/* check to see if there are more queued writes or stream_end was called */
696-
*waiting_writes = !ending_stream && !s_h2_stream_has_outgoing_writes(stream);
697-
}
698-
699686
static struct aws_h2_stream_data_write *s_h2_stream_get_current_write(struct aws_h2_stream *stream) {
700687
AWS_PRECONDITION(s_h2_stream_has_outgoing_writes(stream));
701688
struct aws_linked_list_node *node = aws_linked_list_front(&stream->thread_data.outgoing_writes);
702689
struct aws_h2_stream_data_write *write = AWS_CONTAINER_OF(node, struct aws_h2_stream_data_write, node);
703690
return write;
704691
}
705692

706-
static struct aws_input_stream *s_h2_stream_get_data_stream(struct aws_h2_stream *stream) {
707-
struct aws_h2_stream_data_write *write = s_h2_stream_get_current_write(stream);
708-
return write->data_stream;
709-
}
710-
711-
static bool s_h2_stream_does_current_write_end_stream(struct aws_h2_stream *stream) {
712-
struct aws_h2_stream_data_write *write = s_h2_stream_get_current_write(stream);
713-
return write->end_stream;
714-
}
715-
716693
int aws_h2_stream_on_activated(struct aws_h2_stream *stream, enum aws_h2_stream_body_state *body_state) {
717694
AWS_PRECONDITION_ON_CHANNEL_THREAD(stream);
718695

@@ -799,12 +776,14 @@ int aws_h2_stream_encode_data_frame(
799776
}
800777

801778
*data_encode_status = AWS_H2_DATA_ENCODE_COMPLETE;
802-
struct aws_input_stream *input_stream = s_h2_stream_get_data_stream(stream);
779+
struct aws_h2_stream_data_write *current_write = s_h2_stream_get_current_write(stream);
780+
struct aws_input_stream *input_stream = current_write->data_stream;
803781
AWS_ASSERT(input_stream);
804782

805783
bool input_stream_complete = false;
806784
bool input_stream_stalled = false;
807-
bool ends_stream = s_h2_stream_does_current_write_end_stream(stream);
785+
bool input_stream_failed = false;
786+
bool ends_stream = current_write->end_stream;
808787
if (aws_h2_encode_data_frame(
809788
encoder,
810789
stream->base.id,
@@ -815,20 +794,30 @@ int aws_h2_stream_encode_data_frame(
815794
&connection->thread_data.window_size_peer,
816795
output,
817796
&input_stream_complete,
818-
&input_stream_stalled)) {
797+
&input_stream_stalled,
798+
&input_stream_failed)) {
799+
800+
int error_code = aws_last_error();
801+
802+
/* If error cause caused aws_input_stream, report that specific error in its write-completion callback */
803+
if (input_stream_failed) {
804+
aws_linked_list_remove(&current_write->node);
805+
s_stream_data_write_destroy(stream, current_write, error_code);
806+
}
819807

820808
/* Failed to write DATA, treat it as a Stream Error */
821-
AWS_H2_STREAM_LOGF(ERROR, stream, "Error encoding stream DATA, %s", aws_error_name(aws_last_error()));
822-
struct aws_h2err returned_h2err = s_send_rst_and_close_stream(stream, aws_h2err_from_last_error());
809+
AWS_H2_STREAM_LOGF(ERROR, stream, "Error encoding stream DATA, %s", aws_error_name(error_code));
810+
struct aws_h2err returned_h2err = s_send_rst_and_close_stream(stream, aws_h2err_from_aws_code(error_code));
823811
if (aws_h2err_failed(returned_h2err)) {
824812
aws_h2_connection_shutdown_due_to_write_err(connection, returned_h2err.aws_code);
825813
}
826814
return AWS_OP_SUCCESS;
827815
}
828816

829-
bool waiting_writes = false;
830817
if (input_stream_complete) {
831-
s_h2_stream_write_data_complete(stream, &waiting_writes);
818+
/* finish/clean up the current write operation */
819+
aws_linked_list_remove(&current_write->node);
820+
s_stream_data_write_destroy(stream, current_write, AWS_ERROR_SUCCESS);
832821
}
833822

834823
/*
@@ -867,10 +856,11 @@ int aws_h2_stream_encode_data_frame(
867856
* from outgoing list */
868857
*data_encode_status = AWS_H2_DATA_ENCODE_ONGOING_WINDOW_STALLED;
869858
}
870-
if (waiting_writes) {
859+
if (!s_h2_stream_has_outgoing_writes(stream)) {
871860
/* if window stalled and we waiting for manual writes, we take waiting writes status, which will be handled
872861
* properly if more writes coming, but windows is still stalled. But not the other way around. */
873862
AWS_ASSERT(input_stream_complete);
863+
AWS_ASSERT(!ends_stream);
874864
*data_encode_status = AWS_H2_DATA_ENCODE_ONGOING_WAITING_FOR_WRITES;
875865
}
876866
}

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,7 @@ add_test_case(h2_client_error_from_incoming_headers_callback_reset_stream)
502502
add_test_case(h2_client_error_from_incoming_headers_done_callback_reset_stream)
503503
add_test_case(h2_client_error_from_incoming_body_callback_reset_stream)
504504
add_test_case(h2_client_manual_data_write)
505+
add_test_case(h2_client_manual_data_write_read_broken)
505506
add_test_case(h2_client_manual_data_write_not_enabled)
506507
add_test_case(h2_client_manual_data_write_with_body)
507508
add_test_case(h2_client_manual_data_write_no_data)

tests/fuzz/fuzz_h2_decoder_correct.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
236236

237237
bool body_complete;
238238
bool body_stalled;
239+
bool body_failed;
239240
int32_t stream_window_size_peer = AWS_H2_WINDOW_UPDATE_MAX;
240241
size_t connection_window_size_peer = AWS_H2_WINDOW_UPDATE_MAX;
241242
AWS_FATAL_ASSERT(
@@ -249,7 +250,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
249250
&connection_window_size_peer,
250251
&frame_data,
251252
&body_complete,
252-
&body_stalled) == AWS_OP_SUCCESS);
253+
&body_stalled,
254+
&body_failed) == AWS_OP_SUCCESS);
253255

254256
struct aws_stream_status body_status;
255257
aws_input_stream_get_status(body, &body_status);

tests/h2_test_helper.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,7 @@ int h2_fake_peer_send_data_frame_with_padding_length(
594594

595595
bool body_complete;
596596
bool body_stalled;
597+
bool body_failed;
597598
int32_t stream_window_size_peer = AWS_H2_WINDOW_UPDATE_MAX;
598599
size_t connection_window_size_peer = AWS_H2_WINDOW_UPDATE_MAX;
599600
ASSERT_SUCCESS(aws_h2_encode_data_frame(
@@ -606,10 +607,12 @@ int h2_fake_peer_send_data_frame_with_padding_length(
606607
&connection_window_size_peer,
607608
&msg->message_data,
608609
&body_complete,
609-
&body_stalled));
610+
&body_stalled,
611+
&body_failed));
610612

611613
ASSERT_TRUE(body_complete);
612614
ASSERT_FALSE(body_stalled);
615+
ASSERT_FALSE(body_failed);
613616
ASSERT_TRUE(msg->message_data.len != 0);
614617

615618
ASSERT_SUCCESS(testing_channel_push_read_message(peer->testing_channel, msg));

tests/py_localhost/server.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,9 @@ def window_updated(self, stream_id, delta):
308308
ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
309309
ssl_context.options |= (ssl.OP_NO_COMPRESSION)
310310
ssl_context.load_cert_chain(
311-
certfile="../resources/unittests.crt", keyfile="../resources/unittests.key")
311+
certfile=os.path.join(os.path.dirname(__file__),
312+
"../resources/unittests.crt"),
313+
keyfile=os.path.join(os.path.dirname(__file__), "../resources/unittests.key"))
312314
ssl_context.set_alpn_protocols(["h2"])
313315

314316
loop = asyncio.new_event_loop()

tests/stream_test_helper.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ int client_stream_tester_init(
201201
.on_metrics = s_on_metrics,
202202
.on_complete = s_on_complete,
203203
.on_destroy = s_on_destroy,
204+
.http2_use_manual_data_writes = options->http2_manual_write,
204205
};
205206
tester->stream = aws_http_connection_make_request(options->connection, &request_options);
206207
ASSERT_NOT_NULL(tester->stream);

tests/stream_test_helper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ struct client_stream_tester {
5151
struct client_stream_tester_options {
5252
struct aws_http_message *request;
5353
struct aws_http_connection *connection;
54+
bool http2_manual_write;
5455
};
5556

5657
int client_stream_tester_init(

0 commit comments

Comments
 (0)