Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add if-match to auto-range get #226

Merged
merged 5 commits into from
Nov 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/aws/s3/private/s3_auto_ranged_get.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ struct aws_s3_auto_ranged_get {
} synced_data;

uint32_t initial_message_has_range_header : 1;
uint32_t initial_message_has_if_match_header : 1;

struct aws_string *etag;
};

/* Creates a new auto-ranged get meta request. This will do multiple parallel ranged-gets when appropriate. */
Expand Down
2 changes: 2 additions & 0 deletions include/aws/s3/private/s3_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ extern const struct aws_byte_cursor g_s3_service_name;
AWS_S3_API
extern const struct aws_byte_cursor g_range_header_name;

extern const struct aws_byte_cursor g_if_match_header_name;

AWS_S3_API
extern const struct aws_byte_cursor g_content_range_header_name;

Expand Down
1 change: 1 addition & 0 deletions include/aws/s3/s3.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ enum aws_s3_errors {
AWS_ERROR_S3_LIST_PARTS_PARSE_FAILED,
AWS_ERROR_S3_RESUMED_PART_CHECKSUM_MISMATCH,
AWS_ERROR_S3_RESUME_FAILED,
AWS_ERROR_S3_OBJECT_MODIFIED,
AWS_ERROR_S3_END_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_S3_PACKAGE_ID)
};

Expand Down
1 change: 1 addition & 0 deletions source/s3.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ static struct aws_error_info s_errors[] = {
AWS_DEFINE_ERROR_INFO_S3(AWS_ERROR_S3_LIST_PARTS_PARSE_FAILED, "Failed to parse result from list parts"),
AWS_DEFINE_ERROR_INFO_S3(AWS_ERROR_S3_RESUMED_PART_CHECKSUM_MISMATCH, "Checksum does not match previously uploaded part"),
AWS_DEFINE_ERROR_INFO_S3(AWS_ERROR_S3_RESUME_FAILED, "Resuming request failed"),
AWS_DEFINE_ERROR_INFO_S3(AWS_ERROR_S3_OBJECT_MODIFIED, "The object modifed during download.")
};
/* clang-format on */

Expand Down
40 changes: 40 additions & 0 deletions source/s3_auto_ranged_get.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ struct aws_s3_meta_request *aws_s3_meta_request_auto_ranged_get_new(
AWS_ASSERT(headers != NULL);

auto_ranged_get->initial_message_has_range_header = aws_http_headers_has(headers, g_range_header_name);
auto_ranged_get->initial_message_has_if_match_header = aws_http_headers_has(headers, g_if_match_header_name);

AWS_LOGF_DEBUG(
AWS_LS_S3_META_REQUEST, "id=%p Created new Auto-Ranged Get Meta Request.", (void *)&auto_ranged_get->base);
Expand All @@ -115,6 +116,7 @@ static void s_s3_meta_request_auto_ranged_get_destroy(struct aws_s3_meta_request
AWS_PRECONDITION(meta_request->impl);

struct aws_s3_auto_ranged_get *auto_ranged_get = meta_request->impl;
aws_string_destroy(auto_ranged_get->etag);
aws_mem_release(meta_request->allocator, auto_ranged_get);
}

Expand Down Expand Up @@ -350,6 +352,7 @@ static int s_s3_auto_ranged_get_prepare_request(

/* Generate a new ranged get request based on the original message. */
struct aws_http_message *message = NULL;
struct aws_s3_auto_ranged_get *auto_ranged_get = meta_request->impl;

switch (request->request_tag) {
case AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_HEAD_OBJECT:
Expand Down Expand Up @@ -382,6 +385,19 @@ static int s_s3_auto_ranged_get_prepare_request(
if (meta_request->checksum_config.validate_response_checksum) {
aws_http_headers_set(aws_http_message_get_headers(message), g_request_validation_mode, g_enabled);
}
if (!auto_ranged_get->initial_message_has_if_match_header && auto_ranged_get->etag) {
/* Add the if_match to the request */
AWS_LOGF_DEBUG(
AWS_LS_S3_META_REQUEST,
"id=%p: Added the If-Match header to request %p for part %d",
(void *)meta_request,
(void *)request,
request->part_number);
aws_http_headers_set(
aws_http_message_get_headers(message),
g_if_match_header_name,
aws_byte_cursor_from_string(auto_ranged_get->etag));
}

aws_s3_request_setup_send_data(request, message);
aws_http_message_release(message);
Expand Down Expand Up @@ -574,6 +590,24 @@ static void s_s3_auto_ranged_get_request_finished(
goto update_synced_data;
}

if (!request_failed && !auto_ranged_get->initial_message_has_if_match_header) {
AWS_ASSERT(auto_ranged_get->etag == NULL);
struct aws_byte_cursor etag_header_value;

if (aws_http_headers_get(request->send_data.response_headers, g_etag_header_name, &etag_header_value)) {
aws_raise_error(AWS_ERROR_S3_MISSING_ETAG);
error_code = AWS_ERROR_S3_MISSING_ETAG;
goto update_synced_data;
}

AWS_LOGF_TRACE(
AWS_LS_S3_META_REQUEST,
"id=%p Etag received for the meta request. value is: " PRInSTR "",
(void *)meta_request,
AWS_BYTE_CURSOR_PRI(etag_header_value));
auto_ranged_get->etag = aws_string_new_from_cursor(auto_ranged_get->base.allocator, &etag_header_value);
}

/* If we were able to discover the object-range/content length successfully, then any error code that was passed
* into this function is being handled and does not indicate an overall failure.*/
error_code = AWS_ERROR_SUCCESS;
Expand Down Expand Up @@ -671,6 +705,12 @@ static void s_s3_auto_ranged_get_request_finished(
}

if (error_code != AWS_ERROR_SUCCESS) {
if (error_code == AWS_ERROR_S3_INVALID_RESPONSE_STATUS &&
request->send_data.response_status == AWS_HTTP_STATUS_CODE_412_PRECONDITION_FAILED &&
!auto_ranged_get->initial_message_has_if_match_header) {
/* Use more clear error code as we added the if-match header under the hood. */
error_code = AWS_ERROR_S3_OBJECT_MODIFIED;
}
aws_s3_meta_request_set_fail_synced(meta_request, request, error_code);
if (error_code == AWS_ERROR_S3_RESPONSE_CHECKSUM_MISMATCH) {
/* It's a mismatch of checksum, tell user that we validated the checksum and the algorithm we validated
Expand Down
8 changes: 4 additions & 4 deletions source/s3_request_messages.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ static const struct aws_byte_cursor s_x_amz_meta_prefix = AWS_BYTE_CUR_INIT_FROM
const size_t g_s3_abort_multipart_upload_excluded_headers_count =
AWS_ARRAY_SIZE(g_s3_abort_multipart_upload_excluded_headers);

static int s_s3_message_util_add_content_range_header(
static int s_s3_message_util_add_range_header(
uint64_t part_range_start,
uint64_t part_range_end,
struct aws_http_message *out_message);
Expand All @@ -242,7 +242,7 @@ struct aws_http_message *aws_s3_ranged_get_object_message_new(
return NULL;
}

if (s_s3_message_util_add_content_range_header(range_start, range_end, message)) {
if (s_s3_message_util_add_range_header(range_start, range_end, message)) {
goto error_clean_up;
}

Expand Down Expand Up @@ -995,8 +995,8 @@ int aws_s3_message_util_copy_headers(
return AWS_OP_SUCCESS;
}

/* Add a content-range header.*/
static int s_s3_message_util_add_content_range_header(
/* Add a range header.*/
static int s_s3_message_util_add_range_header(
uint64_t part_range_start,
uint64_t part_range_end,
struct aws_http_message *out_message) {
Expand Down
1 change: 1 addition & 0 deletions source/s3_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const struct aws_byte_cursor g_s3_client_version = AWS_BYTE_CUR_INIT_FROM_STRING
const struct aws_byte_cursor g_s3_service_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("s3");
const struct aws_byte_cursor g_host_header_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Host");
const struct aws_byte_cursor g_range_header_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Range");
const struct aws_byte_cursor g_if_match_header_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("If-Match");
const struct aws_byte_cursor g_etag_header_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("ETag");
const struct aws_byte_cursor g_content_range_header_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Content-Range");
const struct aws_byte_cursor g_content_type_header_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Content-Type");
Expand Down