Skip to content

Commit db41119

Browse files
authored
Destroy callback for http stream (#390)
1 parent 722c82e commit db41119

File tree

13 files changed

+194
-8
lines changed

13 files changed

+194
-8
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ on:
66
- 'main'
77

88
env:
9-
BUILDER_VERSION: v0.9.15
9+
BUILDER_VERSION: v0.9.18
1010
BUILDER_SOURCE: releases
1111
BUILDER_HOST: https://d19elf31gohf1l.cloudfront.net
1212
PACKAGE_NAME: aws-c-http
@@ -117,7 +117,7 @@ jobs:
117117
run: |
118118
python -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder.pyz')"
119119
python builder.pyz build -p ${{ env.PACKAGE_NAME }} --cmake-extra=-DBUILD_SHARED_LIBS=ON
120-
120+
121121
windows-app-verifier:
122122
runs-on: windows-2022 # latest
123123
steps:

include/aws/http/private/request_response_impl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ struct aws_http_stream {
4444
aws_http_on_incoming_header_block_done_fn *on_incoming_header_block_done;
4545
aws_http_on_incoming_body_fn *on_incoming_body;
4646
aws_http_on_stream_complete_fn *on_complete;
47+
aws_http_on_stream_destroy_fn *on_destroy;
4748

4849
struct aws_atomic_var refcount;
4950
enum aws_http_method request_method;

include/aws/http/request_response.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,11 +182,18 @@ typedef int(
182182
typedef int(aws_http_on_incoming_request_done_fn)(struct aws_http_stream *stream, void *user_data);
183183

184184
/**
185-
* Invoked when request/response stream is complete, whether successful or unsuccessful
186-
* This is always invoked on the HTTP connection's event-loop thread.
185+
* Invoked when request/response stream is completely destroyed.
186+
* This may be invoked synchronously when aws_http_stream_release() is called.
187+
* This is invoked even if the stream is never activated.
187188
*/
188189
typedef void(aws_http_on_stream_complete_fn)(struct aws_http_stream *stream, int error_code, void *user_data);
189190

191+
/**
192+
* Invoked when request/response stream destroy completely.
193+
* This can be invoked within the same thead who release the refcount on http stream.
194+
*/
195+
typedef void(aws_http_on_stream_destroy_fn)(void *user_data);
196+
190197
/**
191198
* Options for creating a stream which sends a request from the client and receives a response from the server.
192199
*/
@@ -234,6 +241,9 @@ struct aws_http_make_request_options {
234241
*/
235242
aws_http_on_stream_complete_fn *on_complete;
236243

244+
/* Callback for when the request/response stream is completely destroyed. */
245+
aws_http_on_stream_destroy_fn *on_destroy;
246+
237247
/**
238248
* When using HTTP/2, request body data will be provided over time. The stream will only be polled for writing
239249
* when data has been supplied via `aws_http2_stream_write_data`
@@ -290,6 +300,9 @@ struct aws_http_request_handler_options {
290300
* See `aws_http_on_stream_complete_fn`.
291301
*/
292302
aws_http_on_stream_complete_fn *on_complete;
303+
304+
/* Callback for when the request/response stream is completely destroyed. */
305+
aws_http_on_stream_destroy_fn *on_destroy;
293306
};
294307

295308
/**

source/h1_stream.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,8 @@ static struct aws_h1_stream *s_stream_new_common(
342342
aws_http_on_incoming_headers_fn *on_incoming_headers,
343343
aws_http_on_incoming_header_block_done_fn *on_incoming_header_block_done,
344344
aws_http_on_incoming_body_fn *on_incoming_body,
345-
aws_http_on_stream_complete_fn on_complete) {
345+
aws_http_on_stream_complete_fn *on_complete,
346+
aws_http_on_stream_destroy_fn *on_destroy) {
346347

347348
struct aws_h1_connection *connection = AWS_CONTAINER_OF(connection_base, struct aws_h1_connection, base);
348349

@@ -359,6 +360,7 @@ static struct aws_h1_stream *s_stream_new_common(
359360
stream->base.on_incoming_header_block_done = on_incoming_header_block_done;
360361
stream->base.on_incoming_body = on_incoming_body;
361362
stream->base.on_complete = on_complete;
363+
stream->base.on_destroy = on_destroy;
362364

363365
aws_channel_task_init(
364366
&stream->cross_thread_work_task, s_stream_cross_thread_work_task, stream, "http1_stream_cross_thread_work");
@@ -384,7 +386,8 @@ struct aws_h1_stream *aws_h1_stream_new_request(
384386
options->on_response_headers,
385387
options->on_response_header_block_done,
386388
options->on_response_body,
387-
options->on_complete);
389+
options->on_complete,
390+
options->on_destroy);
388391
if (!stream) {
389392
return NULL;
390393
}
@@ -430,7 +433,8 @@ struct aws_h1_stream *aws_h1_stream_new_request_handler(const struct aws_http_re
430433
options->on_request_headers,
431434
options->on_request_header_block_done,
432435
options->on_request_body,
433-
options->on_complete);
436+
options->on_complete,
437+
options->on_destroy);
434438
if (!stream) {
435439
return NULL;
436440
}

source/h2_stream.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ struct aws_h2_stream *aws_h2_stream_new_request(
241241
stream->base.on_incoming_header_block_done = options->on_response_header_block_done;
242242
stream->base.on_incoming_body = options->on_response_body;
243243
stream->base.on_complete = options->on_complete;
244+
stream->base.on_destroy = options->on_destroy;
244245
stream->base.client_data = &stream->base.client_or_server_data.client;
245246
stream->base.client_data->response_status = AWS_HTTP_STATUS_CODE_UNKNOWN;
246247
struct aws_byte_cursor method;

source/http2_stream_manager.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -804,10 +804,17 @@ static void s_on_stream_complete(struct aws_http_stream *stream, int error_code,
804804
pending_stream_acquisition->options.on_complete(
805805
stream, error_code, pending_stream_acquisition->options.user_data);
806806
}
807-
s_pending_stream_acquisition_destroy(pending_stream_acquisition);
808807
s_sm_connection_on_scheduled_stream_finishes(sm_connection, stream_manager);
809808
}
810809

810+
static void s_on_stream_destroy(void *user_data) {
811+
struct aws_h2_sm_pending_stream_acquisition *pending_stream_acquisition = user_data;
812+
if (pending_stream_acquisition->options.on_destroy) {
813+
pending_stream_acquisition->options.on_destroy(pending_stream_acquisition->options.user_data);
814+
}
815+
s_pending_stream_acquisition_destroy(pending_stream_acquisition);
816+
}
817+
811818
/* Scheduled to happen from connection's thread */
812819
static void s_make_request_task(struct aws_channel_task *task, void *arg, enum aws_task_status status) {
813820
(void)task;
@@ -862,6 +869,7 @@ static void s_make_request_task(struct aws_channel_task *task, void *arg, enum a
862869
.on_response_header_block_done = s_on_incoming_header_block_done,
863870
.on_response_body = s_on_incoming_body,
864871
.on_complete = s_on_stream_complete,
872+
.on_destroy = s_on_stream_destroy,
865873
.user_data = pending_stream_acquisition,
866874
};
867875
/* TODO: we could put the pending acquisition back to the list if the connection is not available for new request.

source/request_response.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,9 +1070,16 @@ void aws_http_stream_release(struct aws_http_stream *stream) {
10701070
if (prev_refcount == 1) {
10711071
AWS_LOGF_TRACE(AWS_LS_HTTP_STREAM, "id=%p: Final stream refcount released.", (void *)stream);
10721072

1073+
void *user_data = stream->user_data;
1074+
aws_http_on_stream_destroy_fn *on_destroy_callback = stream->on_destroy;
1075+
10731076
struct aws_http_connection *owning_connection = stream->owning_connection;
10741077
stream->vtable->destroy(stream);
10751078

1079+
if (on_destroy_callback) {
1080+
/* info user that destroy completed. */
1081+
on_destroy_callback(user_data);
1082+
}
10761083
/* Connection needed to outlive stream, but it's free to go now */
10771084
aws_http_connection_release(owning_connection);
10781085
} else {

tests/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ add_test_case(h1_client_request_send_multiple_chunked_encoding)
100100
add_test_case(h1_client_request_close_header_ends_connection)
101101
add_test_case(h1_client_request_close_header_with_pipelining)
102102
add_test_case(h1_client_request_close_header_with_chunked_encoding_and_pipelining)
103+
add_test_case(h1_client_stream_release_after_complete)
104+
add_test_case(h1_client_stream_release_before_complete)
103105
add_test_case(h1_client_response_get_1liner)
104106
add_test_case(h1_client_response_get_headers)
105107
add_test_case(h1_client_response_get_body)
@@ -375,6 +377,7 @@ add_h2_decoder_test_set(h2_decoder_err_bad_preface_from_client_3)
375377

376378
add_test_case(h2_client_sanity_check)
377379
add_test_case(h2_client_stream_create)
380+
add_test_case(h2_client_stream_release_after_complete)
378381
add_test_case(h2_client_unactivated_stream_cleans_up)
379382
add_test_case(h2_client_connection_preface_sent)
380383
add_test_case(h2_client_auto_ping_ack)

tests/stream_test_helper.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,14 @@ static void s_on_complete(struct aws_http_stream *stream, int error_code, void *
135135
aws_http_stream_get_incoming_response_status(stream, &tester->response_status);
136136
}
137137

138+
static void s_on_destroy(void *user_data) {
139+
struct client_stream_tester *tester = user_data;
140+
141+
/* Validate things are firing properly */
142+
AWS_FATAL_ASSERT(!tester->destroyed);
143+
tester->destroyed = true;
144+
}
145+
138146
int client_stream_tester_init(
139147
struct client_stream_tester *tester,
140148
struct aws_allocator *alloc,
@@ -166,6 +174,7 @@ int client_stream_tester_init(
166174
.on_response_header_block_done = s_on_header_block_done,
167175
.on_response_body = s_on_body,
168176
.on_complete = s_on_complete,
177+
.on_destroy = s_on_destroy,
169178
};
170179
tester->stream = aws_http_connection_make_request(options->connection, &request_options);
171180
ASSERT_NOT_NULL(tester->stream);

tests/stream_test_helper.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ struct client_stream_tester {
4343

4444
/* Whether connection is open when on_complete fires */
4545
bool on_complete_connection_is_open;
46+
47+
bool destroyed;
4648
};
4749

4850
struct client_stream_tester_options {

0 commit comments

Comments
 (0)