Skip to content

Commit 6586c80

Browse files
TingDaoKgraebm
andauthored
report the error back to write complete (#512)
Co-authored-by: Michael Graeb <[email protected]>
1 parent c997d8f commit 6586c80

File tree

7 files changed

+52
-41
lines changed

7 files changed

+52
-41
lines changed

include/aws/http/private/h2_frames.h

+3-1
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

+6-1
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

+22-32
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/fuzz/fuzz_h2_decoder_correct.c

+3-1
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

+4-1
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/test_h2_client.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -5473,8 +5473,8 @@ TEST_CASE(h2_client_manual_data_write_read_broken) {
54735473
ASSERT_TRUE(stream_tester.complete);
54745474
/* The stream complete will get the error code from the input stream read. */
54755475
ASSERT_UINT_EQUALS(stream_tester.on_complete_error_code, AWS_IO_STREAM_READ_FAILED);
5476-
/* The write triggers the stream to complete with error, so the write failed as the stream completes. */
5477-
ASSERT_UINT_EQUALS(error_code, AWS_ERROR_HTTP_STREAM_HAS_COMPLETED);
5476+
/* The write triggers the error, which should be reported to the write complete */
5477+
ASSERT_UINT_EQUALS(error_code, AWS_IO_STREAM_READ_FAILED);
54785478

54795479
aws_http_message_release(request);
54805480

tests/test_h2_encoder.c

+12-3
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ TEST_CASE(h2_encoder_data) {
8686

8787
bool body_complete;
8888
bool body_stalled;
89+
bool body_failed;
8990
int32_t stream_window_size_peer = AWS_H2_WINDOW_UPDATE_MAX;
9091
size_t connection_window_size_peer = AWS_H2_WINDOW_UPDATE_MAX;
9192
ASSERT_SUCCESS(aws_h2_encode_data_frame(
@@ -98,11 +99,13 @@ TEST_CASE(h2_encoder_data) {
9899
&connection_window_size_peer,
99100
&output,
100101
&body_complete,
101-
&body_stalled));
102+
&body_stalled,
103+
&body_failed));
102104

103105
ASSERT_BIN_ARRAYS_EQUALS(expected, sizeof(expected), output.buffer, output.len);
104106
ASSERT_TRUE(body_complete);
105107
ASSERT_FALSE(body_stalled);
108+
ASSERT_FALSE(body_failed);
106109

107110
aws_byte_buf_clean_up(&output);
108111
aws_input_stream_release(body);
@@ -140,6 +143,7 @@ TEST_CASE(h2_encoder_data_stalled) {
140143

141144
bool body_complete;
142145
bool body_stalled;
146+
bool body_failed;
143147
int32_t stream_window_size_peer = AWS_H2_WINDOW_UPDATE_MAX;
144148
size_t connection_window_size_peer = AWS_H2_WINDOW_UPDATE_MAX;
145149
ASSERT_SUCCESS(aws_h2_encode_data_frame(
@@ -152,11 +156,13 @@ TEST_CASE(h2_encoder_data_stalled) {
152156
&connection_window_size_peer,
153157
&output,
154158
&body_complete,
155-
&body_stalled));
159+
&body_stalled,
160+
&body_failed));
156161

157162
ASSERT_BIN_ARRAYS_EQUALS(expected, sizeof(expected), output.buffer, output.len);
158163
ASSERT_FALSE(body_complete);
159164
ASSERT_TRUE(body_stalled);
165+
ASSERT_FALSE(body_failed);
160166

161167
aws_byte_buf_clean_up(&output);
162168
aws_input_stream_release(body);
@@ -182,6 +188,7 @@ TEST_CASE(h2_encoder_data_stalled_completely) {
182188

183189
bool body_complete;
184190
bool body_stalled;
191+
bool body_failed;
185192
int32_t stream_window_size_peer = AWS_H2_WINDOW_UPDATE_MAX;
186193
size_t connection_window_size_peer = AWS_H2_WINDOW_UPDATE_MAX;
187194
ASSERT_SUCCESS(aws_h2_encode_data_frame(
@@ -194,10 +201,12 @@ TEST_CASE(h2_encoder_data_stalled_completely) {
194201
&connection_window_size_peer,
195202
&output,
196203
&body_complete,
197-
&body_stalled));
204+
&body_stalled,
205+
&body_failed));
198206

199207
ASSERT_FALSE(body_complete);
200208
ASSERT_TRUE(body_stalled);
209+
ASSERT_FALSE(body_failed);
201210
ASSERT_UINT_EQUALS(0, output.len);
202211

203212
/* clean up */

0 commit comments

Comments
 (0)