Skip to content

Commit

Permalink
support if-none-match for upload (#462)
Browse files Browse the repository at this point in the history
  • Loading branch information
TingDaoK authored Nov 26, 2024
1 parent 5877f40 commit e373ef4
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ on:
branches-ignore:
- 'main'

# cancel in-progress builds after a new commit
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
BUILDER_VERSION: v0.9.64
BUILDER_SOURCE: releases
Expand Down
3 changes: 3 additions & 0 deletions source/s3_request_messages.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const struct aws_byte_cursor g_s3_create_multipart_upload_excluded_headers[] = {
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Content-MD5"),
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-copy-source"),
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-copy-source-range"),
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("if-none-match"),
};

const size_t g_s3_create_multipart_upload_excluded_headers_count =
Expand Down Expand Up @@ -49,6 +50,7 @@ const struct aws_byte_cursor g_s3_upload_part_excluded_headers[] = {
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-object-lock-mode"),
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-object-lock-retain-until-date"),
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-object-lock-legal-hold"),
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("if-none-match"),
};

const size_t g_s3_upload_part_excluded_headers_count = AWS_ARRAY_SIZE(g_s3_upload_part_excluded_headers);
Expand Down Expand Up @@ -211,6 +213,7 @@ const struct aws_byte_cursor g_s3_abort_multipart_upload_excluded_headers[] = {
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-object-lock-legal-hold"),
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-copy-source"),
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-copy-source-range"),
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("if-none-match"),
};

static const struct aws_byte_cursor s_x_amz_meta_prefix = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-meta-");
Expand Down
2 changes: 2 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ add_net_test_case(test_s3_put_object_async_no_content_length_1part)
add_net_test_case(test_s3_put_object_async_no_content_length_empty_part2)
add_net_test_case(test_s3_put_object_async_no_content_length_2parts)
add_net_test_case(test_s3_put_object_async_fail_reading)
add_net_test_case(test_s3_put_object_if_none_match)
add_net_test_case(test_s3_put_object_mpu_if_none_match)
add_net_test_case(test_s3_many_async_uploads_without_data)
add_net_test_case(test_s3_download_empty_file_with_checksum)
add_net_test_case(test_s3_download_empty_file_with_checksum_header)
Expand Down
87 changes: 87 additions & 0 deletions tests/s3_data_plane_tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -3010,6 +3010,93 @@ static int s_test_s3_put_object_async_fail_reading(struct aws_allocator *allocat
return 0;
}

static int s_test_validate_if_none_match_failure_response(struct aws_s3_meta_request_test_results *test_results) {

/**
* response body should be like:
* <Error>
* <Code>PreconditionFailed</Code>
* <Message>At least one of the pre-conditions you specified did not hold</Message>
* <Condition>If-None-Match</Condition>
* <RequestId></RequestId>
* <HostId></HostId>
* </Error>
*/

struct aws_byte_cursor xml_doc = aws_byte_cursor_from_buf(&test_results->error_response_body);
struct aws_byte_cursor error_code_string = {0};
struct aws_byte_cursor condition_string = {0};

const char *error_code_path[] = {"Error", "Code", NULL};
ASSERT_SUCCESS(aws_xml_get_body_at_path(test_results->allocator, xml_doc, error_code_path, &error_code_string));
ASSERT_TRUE(aws_byte_cursor_eq_c_str(&error_code_string, "PreconditionFailed"));

const char *condition_path[] = {"Error", "Condition", NULL};
ASSERT_SUCCESS(aws_xml_get_body_at_path(test_results->allocator, xml_doc, condition_path, &condition_string));
ASSERT_TRUE(aws_byte_cursor_eq_c_str(&condition_string, "If-None-Match"));

return AWS_OP_SUCCESS;
}

AWS_TEST_CASE(test_s3_put_object_if_none_match, s_test_s3_put_object_if_none_match)
static int s_test_s3_put_object_if_none_match(struct aws_allocator *allocator, void *ctx) {
(void)ctx;

struct aws_s3_meta_request_test_results test_results;
aws_s3_meta_request_test_results_init(&test_results, allocator);
struct aws_byte_cursor if_none_match_all = aws_byte_cursor_from_c_str("*");
struct aws_s3_tester_meta_request_options put_options = {
.allocator = allocator,
.meta_request_type = AWS_S3_META_REQUEST_TYPE_PUT_OBJECT,
.validate_type = AWS_S3_TESTER_VALIDATE_TYPE_EXPECT_FAILURE,
.put_options =
{
/* Use pre_exist object so that the request should fail with the expected failure message. */
.object_path_override = g_pre_existing_object_1MB,
.object_size_mb = 1,
.if_none_match_header = if_none_match_all,
},
};
ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(NULL, &put_options, &test_results));

ASSERT_UINT_EQUALS(AWS_HTTP_STATUS_CODE_412_PRECONDITION_FAILED, test_results.finished_response_status);
ASSERT_SUCCESS(s_test_validate_if_none_match_failure_response(&test_results));

aws_s3_meta_request_test_results_clean_up(&test_results);
return 0;
}

AWS_TEST_CASE(test_s3_put_object_mpu_if_none_match, s_test_s3_put_object_mpu_if_none_match)
static int s_test_s3_put_object_mpu_if_none_match(struct aws_allocator *allocator, void *ctx) {
(void)ctx;

struct aws_s3_meta_request_test_results test_results;
aws_s3_meta_request_test_results_init(&test_results, allocator);
struct aws_byte_cursor if_none_match_all = aws_byte_cursor_from_c_str("*");
struct aws_s3_tester_meta_request_options put_options = {
.allocator = allocator,
.meta_request_type = AWS_S3_META_REQUEST_TYPE_PUT_OBJECT,
.validate_type = AWS_S3_TESTER_VALIDATE_TYPE_EXPECT_FAILURE,
.put_options =
{
/* Use pre_exist object so that the request should fail with the expected failure message. */
.object_path_override = g_pre_existing_object_10MB,
.object_size_mb = 10,
.if_none_match_header = if_none_match_all,
},
};
ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(NULL, &put_options, &test_results));

/** Complete MPU can fail with 200 error */
ASSERT_TRUE(
AWS_HTTP_STATUS_CODE_412_PRECONDITION_FAILED == test_results.finished_response_status ||
AWS_HTTP_STATUS_CODE_200_OK == test_results.finished_response_status);
ASSERT_SUCCESS(s_test_validate_if_none_match_failure_response(&test_results));

aws_s3_meta_request_test_results_clean_up(&test_results);
return 0;
}

AWS_TEST_CASE(test_s3_put_object_sse_kms, s_test_s3_put_object_sse_kms)
static int s_test_s3_put_object_sse_kms(struct aws_allocator *allocator, void *ctx) {
(void)ctx;
Expand Down
7 changes: 7 additions & 0 deletions tests/s3_tester.c
Original file line number Diff line number Diff line change
Expand Up @@ -1696,6 +1696,13 @@ int aws_s3_tester_send_meta_request_with_options(
aws_http_message_add_header(message, content_encoding_header);
}

if (options->put_options.if_none_match_header.ptr != NULL) {
struct aws_http_header if_none_match_header = {
.name = aws_byte_cursor_from_c_str("if-none-match"),
.value = options->put_options.if_none_match_header,
};
aws_http_message_add_header(message, if_none_match_header);
}
meta_request_options.message = message;
aws_byte_buf_clean_up(&object_path_buffer);
}
Expand Down
1 change: 1 addition & 0 deletions tests/s3_tester.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ struct aws_s3_tester_meta_request_options {
size_t content_length;
bool skip_content_length;
struct aws_byte_cursor content_encoding;
struct aws_byte_cursor if_none_match_header;
} put_options;

enum aws_s3_tester_sse_type sse_type;
Expand Down

0 comments on commit e373ef4

Please sign in to comment.