Skip to content

Commit 3afa5d0

Browse files
authored
[fix] retry with checksum result in failure (#543)
1 parent c27e829 commit 3afa5d0

15 files changed

+325
-177
lines changed

include/aws/s3/private/s3_checksum_context.h

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "aws/s3/s3_client.h"
1010
#include <aws/common/byte_buf.h>
11+
#include <aws/common/mutex.h>
1112
#include <aws/common/ref_count.h>
1213

1314
struct aws_s3_meta_request_checksum_config_storage;
@@ -28,9 +29,14 @@ struct aws_s3_upload_request_checksum_context {
2829
enum aws_s3_checksum_algorithm algorithm;
2930
enum aws_s3_checksum_location location;
3031

31-
struct aws_byte_buf base64_checksum;
32-
/* The checksum already be calculated or not. */
33-
bool checksum_calculated;
32+
struct {
33+
/* Note: don't directly access the synced_data. */
34+
/* Lock to make sure the checksum context is safe to be access from different threads. */
35+
struct aws_mutex lock;
36+
struct aws_byte_buf base64_checksum;
37+
/* The checksum already be calculated or not. */
38+
bool checksum_calculated;
39+
} synced_data;
3440

3541
/* Validation */
3642
size_t encoded_checksum_size;
@@ -96,8 +102,7 @@ struct aws_s3_upload_request_checksum_context *aws_s3_upload_request_checksum_co
96102
* @return true if checksum calculation is needed, false otherwise
97103
*/
98104
AWS_S3_API
99-
bool aws_s3_upload_request_checksum_context_should_calculate(
100-
const struct aws_s3_upload_request_checksum_context *context);
105+
bool aws_s3_upload_request_checksum_context_should_calculate(struct aws_s3_upload_request_checksum_context *context);
101106

102107
/**
103108
* Check if checksum should be added to HTTP headers.
@@ -122,15 +127,18 @@ bool aws_s3_upload_request_checksum_context_should_add_trailer(
122127
const struct aws_s3_upload_request_checksum_context *context);
123128

124129
/**
125-
* Get the checksum buffer to use for output.
126-
* Returns the internal buffer for storing the calculated checksum.
130+
* Encode the checksum to base64 and store it in the context.
131+
* This function is thread-safe and can be called from multiple threads.
132+
* Returns AWS_OP_SUCCESS on success, AWS_OP_ERR otherwise
127133
*
128134
* @param context The checksum context
129-
* @return Pointer to the checksum buffer, or NULL if context is invalid
135+
* @param raw_checksum_cursor the byte cursor to the raw checksum value.
136+
* @return AWS_OP_SUCCESS on success, AWS_OP_ERR otherwise
130137
*/
131138
AWS_S3_API
132-
struct aws_byte_buf *aws_s3_upload_request_checksum_context_get_output_buffer(
133-
struct aws_s3_upload_request_checksum_context *context);
139+
int aws_s3_upload_request_checksum_context_finalize_checksum(
140+
struct aws_s3_upload_request_checksum_context *context,
141+
struct aws_byte_cursor raw_checksum_cursor);
134142

135143
/**
136144
* Get a cursor to the current base64 encoded checksum value (for use in headers/trailers).
@@ -141,7 +149,7 @@ struct aws_byte_buf *aws_s3_upload_request_checksum_context_get_output_buffer(
141149
*/
142150
AWS_S3_API
143151
struct aws_byte_cursor aws_s3_upload_request_checksum_context_get_checksum_cursor(
144-
const struct aws_s3_upload_request_checksum_context *context);
152+
struct aws_s3_upload_request_checksum_context *context);
145153

146154
AWS_EXTERN_C_END
147155

include/aws/s3/private/s3_checksums.h

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,7 @@ struct aws_s3_meta_request_checksum_config_storage {
6262
};
6363

6464
/**
65-
* a stream that takes in a stream, computes a running checksum as it is read, and outputs the checksum when the stream
66-
* is destroyed.
65+
* a stream that takes in a stream
6766
* Note: seek this stream will immediately fail, as it would prevent an accurate calculation of the
6867
* checksum.
6968
*
@@ -72,15 +71,28 @@ struct aws_s3_meta_request_checksum_config_storage {
7271
* outputs the checksum of existing stream to checksum_output upon destruction. Will be kept
7372
* alive by the checksum stream
7473
* @param algorithm Checksum algorithm to use.
75-
* @param checksum_output Checksum of the `existing_stream`, owned by caller, which will be calculated when this stream
76-
* is destroyed.
7774
*/
7875
AWS_S3_API
7976
struct aws_input_stream *aws_checksum_stream_new(
8077
struct aws_allocator *allocator,
8178
struct aws_input_stream *existing_stream,
82-
enum aws_s3_checksum_algorithm algorithm,
83-
struct aws_byte_buf *checksum_output);
79+
enum aws_s3_checksum_algorithm algorithm);
80+
81+
/**
82+
* Finalize the checksum has read so far to the output checksum buf with base64 encoding.
83+
* Not thread safe.
84+
*/
85+
AWS_S3_API
86+
int aws_checksum_stream_finalize_checksum(struct aws_input_stream *checksum_stream, struct aws_byte_buf *checksum_buf);
87+
88+
/**
89+
* Finalize the checksum has read so far to the checksum context.
90+
* Not thread safe.
91+
*/
92+
AWS_S3_API
93+
int aws_checksum_stream_finalize_checksum_context(
94+
struct aws_input_stream *checksum_stream,
95+
struct aws_s3_upload_request_checksum_context *checksum_context);
8496

8597
/**
8698
* TODO: properly support chunked encoding.

include/aws/s3/private/s3_client_impl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ struct aws_s3_client_vtable {
175175
struct aws_http_connection *client_connection,
176176
const struct aws_http_make_request_options *options);
177177

178-
void (*after_prepare_upload_part_finish)(struct aws_s3_request *request);
178+
void (*after_prepare_upload_part_finish)(struct aws_s3_request *request, struct aws_http_message *message);
179179
};
180180

181181
struct aws_s3_upload_part_timeout_stats {

include/aws/s3/s3_client.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -884,7 +884,7 @@ struct aws_s3_meta_request_options {
884884
* Optional.
885885
* Callback for reviewing an upload before it completes.
886886
* WARNING: experimental/unstable
887-
* See `aws_s3_upload_review_fn`
887+
* See `aws_s3_meta_request_upload_review_fn`
888888
*/
889889
aws_s3_meta_request_upload_review_fn *upload_review_callback;
890890

source/s3_auto_ranged_put.c

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,15 +1099,20 @@ static void s_s3_prepare_upload_part_on_read_done(void *user_data) {
10991099
goto on_done;
11001100
}
11011101
struct aws_s3_upload_request_checksum_context *context = previously_uploaded_info->checksum_context;
1102-
/* if previously uploaded part had a checksum, compare it to what we just skipped */
1103-
if (context != NULL && context->checksum_calculated == true &&
1104-
s_verify_part_matches_checksum(
1105-
meta_request->allocator,
1106-
aws_byte_cursor_from_buf(&request->request_body),
1107-
meta_request->checksum_config.checksum_algorithm,
1108-
aws_byte_cursor_from_buf(&context->base64_checksum))) {
1109-
error_code = aws_last_error_or_unknown();
1110-
goto on_done;
1102+
if (context) {
1103+
if (!aws_s3_upload_request_checksum_context_should_calculate(context)) {
1104+
struct aws_byte_cursor previous_calculated_checksum =
1105+
aws_s3_upload_request_checksum_context_get_checksum_cursor(context);
1106+
/* if previously uploaded part had a checksum, compare it to what we just skipped */
1107+
if (s_verify_part_matches_checksum(
1108+
meta_request->allocator,
1109+
aws_byte_cursor_from_buf(&request->request_body),
1110+
meta_request->checksum_config.checksum_algorithm,
1111+
previous_calculated_checksum) != AWS_OP_SUCCESS) {
1112+
error_code = aws_last_error_or_unknown();
1113+
goto on_done;
1114+
}
1115+
}
11111116
}
11121117
}
11131118

@@ -1157,9 +1162,6 @@ static void s_s3_prepare_upload_part_finish(struct aws_s3_prepare_upload_part_jo
11571162
checksum_context = part->checksum_context;
11581163
/* If checksum already calculated, it means either the part being retried or the part resumed from list
11591164
* parts. Keep reusing the old checksum in case of the request body in memory mangled */
1160-
AWS_ASSERT(
1161-
!checksum_context->checksum_calculated || request->num_times_prepared > 0 ||
1162-
auto_ranged_put->resume_token != NULL);
11631165
aws_s3_meta_request_unlock_synced_data(meta_request);
11641166
}
11651167
/* END CRITICAL SECTION */
@@ -1184,15 +1186,15 @@ static void s_s3_prepare_upload_part_finish(struct aws_s3_prepare_upload_part_jo
11841186
aws_future_http_message_set_error(part_prep->on_complete, aws_last_error());
11851187
goto on_done;
11861188
}
1189+
if (client->vtable->after_prepare_upload_part_finish) {
1190+
/* TEST ONLY, allow test to stub here. */
1191+
client->vtable->after_prepare_upload_part_finish(request, message);
1192+
}
11871193

11881194
/* Success! */
11891195
aws_future_http_message_set_result_by_move(part_prep->on_complete, &message);
11901196

11911197
on_done:
1192-
if (client->vtable->after_prepare_upload_part_finish) {
1193-
/* TEST ONLY, allow test to stub here. */
1194-
client->vtable->after_prepare_upload_part_finish(request);
1195-
}
11961198
AWS_FATAL_ASSERT(aws_future_http_message_is_done(part_prep->on_complete));
11971199
aws_future_bool_release(part_prep->asyncstep_read_part);
11981200
aws_future_http_message_release(part_prep->on_complete);

source/s3_checksum_context.c

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,18 @@
88
#include <aws/common/encoding.h>
99
#include <aws/common/logging.h>
1010

11+
static void s_lock_synced_data(struct aws_s3_upload_request_checksum_context *context) {
12+
aws_mutex_lock(&context->synced_data.lock);
13+
}
14+
15+
static void s_unlock_synced_data(struct aws_s3_upload_request_checksum_context *context) {
16+
aws_mutex_unlock(&context->synced_data.lock);
17+
}
18+
1119
static void s_aws_s3_upload_request_checksum_context_destroy(void *context) {
1220
struct aws_s3_upload_request_checksum_context *checksum_context = context;
13-
aws_byte_buf_clean_up(&checksum_context->base64_checksum);
21+
aws_byte_buf_clean_up(&checksum_context->synced_data.base64_checksum);
22+
aws_mutex_clean_up(&checksum_context->synced_data.lock);
1423
aws_mem_release(checksum_context->allocator, checksum_context);
1524
}
1625

@@ -24,6 +33,10 @@ static struct aws_s3_upload_request_checksum_context *s_s3_upload_request_checks
2433

2534
aws_ref_count_init(&context->ref_count, context, s_aws_s3_upload_request_checksum_context_destroy);
2635
context->allocator = allocator;
36+
if (aws_mutex_init(&context->synced_data.lock)) {
37+
aws_s3_upload_request_checksum_context_release(context);
38+
return NULL;
39+
}
2740
/* Handle case where no checksum config is provided */
2841
if (!checksum_config || checksum_config->checksum_algorithm == AWS_SCA_NONE) {
2942
context->algorithm = AWS_SCA_NONE;
@@ -54,7 +67,7 @@ struct aws_s3_upload_request_checksum_context *aws_s3_upload_request_checksum_co
5467
s_s3_upload_request_checksum_context_new_base(allocator, checksum_config);
5568
if (context && context->encoded_checksum_size > 0) {
5669
/* Initial the buffer for checksum */
57-
aws_byte_buf_init(&context->base64_checksum, allocator, context->encoded_checksum_size);
70+
aws_byte_buf_init(&context->synced_data.base64_checksum, allocator, context->encoded_checksum_size);
5871
}
5972
return context;
6073
}
@@ -79,8 +92,8 @@ struct aws_s3_upload_request_checksum_context *aws_s3_upload_request_checksum_co
7992
aws_s3_upload_request_checksum_context_release(context);
8093
return NULL;
8194
}
82-
aws_byte_buf_init_copy_from_cursor(&context->base64_checksum, allocator, existing_base64_checksum);
83-
context->checksum_calculated = true;
95+
aws_byte_buf_init_copy_from_cursor(&context->synced_data.base64_checksum, allocator, existing_base64_checksum);
96+
context->synced_data.checksum_calculated = true;
8497
}
8598
return context;
8699
}
@@ -101,14 +114,18 @@ struct aws_s3_upload_request_checksum_context *aws_s3_upload_request_checksum_co
101114
return NULL;
102115
}
103116

104-
bool aws_s3_upload_request_checksum_context_should_calculate(
105-
const struct aws_s3_upload_request_checksum_context *context) {
117+
bool aws_s3_upload_request_checksum_context_should_calculate(struct aws_s3_upload_request_checksum_context *context) {
106118
if (!context || context->algorithm == AWS_SCA_NONE) {
107119
return false;
108120
}
109121

122+
bool should_calculate = false;
123+
s_lock_synced_data(context);
110124
/* If not previous calculated */
111-
return !context->checksum_calculated;
125+
should_calculate = !context->synced_data.checksum_calculated;
126+
s_unlock_synced_data(context);
127+
128+
return should_calculate;
112129
}
113130

114131
bool aws_s3_upload_request_checksum_context_should_add_header(
@@ -129,19 +146,46 @@ bool aws_s3_upload_request_checksum_context_should_add_trailer(
129146
return context->location == AWS_SCL_TRAILER && context->algorithm != AWS_SCA_NONE;
130147
}
131148

132-
struct aws_byte_buf *aws_s3_upload_request_checksum_context_get_output_buffer(
133-
struct aws_s3_upload_request_checksum_context *context) {
149+
int aws_s3_upload_request_checksum_context_finalize_checksum(
150+
struct aws_s3_upload_request_checksum_context *context,
151+
struct aws_byte_cursor raw_checksum_cursor) {
134152
if (!context) {
135-
return NULL;
153+
return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
136154
}
137-
return &context->base64_checksum;
155+
s_lock_synced_data(context);
156+
/* If not previous calculated */
157+
if (!context->synced_data.checksum_calculated) {
158+
AWS_ASSERT(context->synced_data.base64_checksum.len == 0);
159+
160+
if (aws_base64_encode(&raw_checksum_cursor, &context->synced_data.base64_checksum)) {
161+
aws_byte_buf_reset(&context->synced_data.base64_checksum, false);
162+
AWS_LOGF_ERROR(
163+
AWS_LS_S3_GENERAL,
164+
"Failed to base64 encode for the checksum. Raw checksum length: %zu. Output buffer capacity: %zu "
165+
"length %zu",
166+
raw_checksum_cursor.len,
167+
context->synced_data.base64_checksum.capacity,
168+
context->synced_data.base64_checksum.len);
169+
s_unlock_synced_data(context);
170+
return AWS_OP_ERR;
171+
}
172+
context->synced_data.checksum_calculated = true;
173+
}
174+
s_unlock_synced_data(context);
175+
return AWS_OP_SUCCESS;
138176
}
139177

140178
struct aws_byte_cursor aws_s3_upload_request_checksum_context_get_checksum_cursor(
141-
const struct aws_s3_upload_request_checksum_context *context) {
179+
struct aws_s3_upload_request_checksum_context *context) {
142180
struct aws_byte_cursor checksum_cursor = {0};
143-
if (!context || !context->checksum_calculated) {
181+
if (!context) {
144182
return checksum_cursor;
145183
}
146-
return aws_byte_cursor_from_buf(&context->base64_checksum);
184+
s_lock_synced_data(context);
185+
/* If not previous calculated */
186+
if (context->synced_data.checksum_calculated) {
187+
checksum_cursor = aws_byte_cursor_from_buf(&context->synced_data.base64_checksum);
188+
}
189+
s_unlock_synced_data(context);
190+
return checksum_cursor;
147191
}

0 commit comments

Comments
 (0)