Skip to content

Commit e373ef4

Browse files
authored
support if-none-match for upload (#462)
1 parent 5877f40 commit e373ef4

File tree

6 files changed

+105
-0
lines changed

6 files changed

+105
-0
lines changed

.github/workflows/ci.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ on:
55
branches-ignore:
66
- 'main'
77

8+
# cancel in-progress builds after a new commit
9+
concurrency:
10+
group: ${{ github.workflow }}-${{ github.ref }}
11+
cancel-in-progress: true
12+
813
env:
914
BUILDER_VERSION: v0.9.64
1015
BUILDER_SOURCE: releases

source/s3_request_messages.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const struct aws_byte_cursor g_s3_create_multipart_upload_excluded_headers[] = {
2020
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Content-MD5"),
2121
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-copy-source"),
2222
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-copy-source-range"),
23+
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("if-none-match"),
2324
};
2425

2526
const size_t g_s3_create_multipart_upload_excluded_headers_count =
@@ -49,6 +50,7 @@ const struct aws_byte_cursor g_s3_upload_part_excluded_headers[] = {
4950
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-object-lock-mode"),
5051
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-object-lock-retain-until-date"),
5152
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-object-lock-legal-hold"),
53+
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("if-none-match"),
5254
};
5355

5456
const size_t g_s3_upload_part_excluded_headers_count = AWS_ARRAY_SIZE(g_s3_upload_part_excluded_headers);
@@ -211,6 +213,7 @@ const struct aws_byte_cursor g_s3_abort_multipart_upload_excluded_headers[] = {
211213
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-object-lock-legal-hold"),
212214
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-copy-source"),
213215
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-copy-source-range"),
216+
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("if-none-match"),
214217
};
215218

216219
static const struct aws_byte_cursor s_x_amz_meta_prefix = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-meta-");

tests/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ add_net_test_case(test_s3_put_object_async_no_content_length_1part)
140140
add_net_test_case(test_s3_put_object_async_no_content_length_empty_part2)
141141
add_net_test_case(test_s3_put_object_async_no_content_length_2parts)
142142
add_net_test_case(test_s3_put_object_async_fail_reading)
143+
add_net_test_case(test_s3_put_object_if_none_match)
144+
add_net_test_case(test_s3_put_object_mpu_if_none_match)
143145
add_net_test_case(test_s3_many_async_uploads_without_data)
144146
add_net_test_case(test_s3_download_empty_file_with_checksum)
145147
add_net_test_case(test_s3_download_empty_file_with_checksum_header)

tests/s3_data_plane_tests.c

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3010,6 +3010,93 @@ static int s_test_s3_put_object_async_fail_reading(struct aws_allocator *allocat
30103010
return 0;
30113011
}
30123012

3013+
static int s_test_validate_if_none_match_failure_response(struct aws_s3_meta_request_test_results *test_results) {
3014+
3015+
/**
3016+
* response body should be like:
3017+
* <Error>
3018+
* <Code>PreconditionFailed</Code>
3019+
* <Message>At least one of the pre-conditions you specified did not hold</Message>
3020+
* <Condition>If-None-Match</Condition>
3021+
* <RequestId></RequestId>
3022+
* <HostId></HostId>
3023+
* </Error>
3024+
*/
3025+
3026+
struct aws_byte_cursor xml_doc = aws_byte_cursor_from_buf(&test_results->error_response_body);
3027+
struct aws_byte_cursor error_code_string = {0};
3028+
struct aws_byte_cursor condition_string = {0};
3029+
3030+
const char *error_code_path[] = {"Error", "Code", NULL};
3031+
ASSERT_SUCCESS(aws_xml_get_body_at_path(test_results->allocator, xml_doc, error_code_path, &error_code_string));
3032+
ASSERT_TRUE(aws_byte_cursor_eq_c_str(&error_code_string, "PreconditionFailed"));
3033+
3034+
const char *condition_path[] = {"Error", "Condition", NULL};
3035+
ASSERT_SUCCESS(aws_xml_get_body_at_path(test_results->allocator, xml_doc, condition_path, &condition_string));
3036+
ASSERT_TRUE(aws_byte_cursor_eq_c_str(&condition_string, "If-None-Match"));
3037+
3038+
return AWS_OP_SUCCESS;
3039+
}
3040+
3041+
AWS_TEST_CASE(test_s3_put_object_if_none_match, s_test_s3_put_object_if_none_match)
3042+
static int s_test_s3_put_object_if_none_match(struct aws_allocator *allocator, void *ctx) {
3043+
(void)ctx;
3044+
3045+
struct aws_s3_meta_request_test_results test_results;
3046+
aws_s3_meta_request_test_results_init(&test_results, allocator);
3047+
struct aws_byte_cursor if_none_match_all = aws_byte_cursor_from_c_str("*");
3048+
struct aws_s3_tester_meta_request_options put_options = {
3049+
.allocator = allocator,
3050+
.meta_request_type = AWS_S3_META_REQUEST_TYPE_PUT_OBJECT,
3051+
.validate_type = AWS_S3_TESTER_VALIDATE_TYPE_EXPECT_FAILURE,
3052+
.put_options =
3053+
{
3054+
/* Use pre_exist object so that the request should fail with the expected failure message. */
3055+
.object_path_override = g_pre_existing_object_1MB,
3056+
.object_size_mb = 1,
3057+
.if_none_match_header = if_none_match_all,
3058+
},
3059+
};
3060+
ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(NULL, &put_options, &test_results));
3061+
3062+
ASSERT_UINT_EQUALS(AWS_HTTP_STATUS_CODE_412_PRECONDITION_FAILED, test_results.finished_response_status);
3063+
ASSERT_SUCCESS(s_test_validate_if_none_match_failure_response(&test_results));
3064+
3065+
aws_s3_meta_request_test_results_clean_up(&test_results);
3066+
return 0;
3067+
}
3068+
3069+
AWS_TEST_CASE(test_s3_put_object_mpu_if_none_match, s_test_s3_put_object_mpu_if_none_match)
3070+
static int s_test_s3_put_object_mpu_if_none_match(struct aws_allocator *allocator, void *ctx) {
3071+
(void)ctx;
3072+
3073+
struct aws_s3_meta_request_test_results test_results;
3074+
aws_s3_meta_request_test_results_init(&test_results, allocator);
3075+
struct aws_byte_cursor if_none_match_all = aws_byte_cursor_from_c_str("*");
3076+
struct aws_s3_tester_meta_request_options put_options = {
3077+
.allocator = allocator,
3078+
.meta_request_type = AWS_S3_META_REQUEST_TYPE_PUT_OBJECT,
3079+
.validate_type = AWS_S3_TESTER_VALIDATE_TYPE_EXPECT_FAILURE,
3080+
.put_options =
3081+
{
3082+
/* Use pre_exist object so that the request should fail with the expected failure message. */
3083+
.object_path_override = g_pre_existing_object_10MB,
3084+
.object_size_mb = 10,
3085+
.if_none_match_header = if_none_match_all,
3086+
},
3087+
};
3088+
ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(NULL, &put_options, &test_results));
3089+
3090+
/** Complete MPU can fail with 200 error */
3091+
ASSERT_TRUE(
3092+
AWS_HTTP_STATUS_CODE_412_PRECONDITION_FAILED == test_results.finished_response_status ||
3093+
AWS_HTTP_STATUS_CODE_200_OK == test_results.finished_response_status);
3094+
ASSERT_SUCCESS(s_test_validate_if_none_match_failure_response(&test_results));
3095+
3096+
aws_s3_meta_request_test_results_clean_up(&test_results);
3097+
return 0;
3098+
}
3099+
30133100
AWS_TEST_CASE(test_s3_put_object_sse_kms, s_test_s3_put_object_sse_kms)
30143101
static int s_test_s3_put_object_sse_kms(struct aws_allocator *allocator, void *ctx) {
30153102
(void)ctx;

tests/s3_tester.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1696,6 +1696,13 @@ int aws_s3_tester_send_meta_request_with_options(
16961696
aws_http_message_add_header(message, content_encoding_header);
16971697
}
16981698

1699+
if (options->put_options.if_none_match_header.ptr != NULL) {
1700+
struct aws_http_header if_none_match_header = {
1701+
.name = aws_byte_cursor_from_c_str("if-none-match"),
1702+
.value = options->put_options.if_none_match_header,
1703+
};
1704+
aws_http_message_add_header(message, if_none_match_header);
1705+
}
16991706
meta_request_options.message = message;
17001707
aws_byte_buf_clean_up(&object_path_buffer);
17011708
}

tests/s3_tester.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ struct aws_s3_tester_meta_request_options {
219219
size_t content_length;
220220
bool skip_content_length;
221221
struct aws_byte_cursor content_encoding;
222+
struct aws_byte_cursor if_none_match_header;
222223
} put_options;
223224

224225
enum aws_s3_tester_sse_type sse_type;

0 commit comments

Comments
 (0)