123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722 |
- /**
- * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0.
- */
- #include "aws/s3/private/s3_auto_ranged_get.h"
- #include "aws/s3/private/s3_client_impl.h"
- #include "aws/s3/private/s3_meta_request_impl.h"
- #include "aws/s3/private/s3_request_messages.h"
- #include "aws/s3/private/s3_util.h"
- #include <aws/common/string.h>
- #include <inttypes.h>
- #ifdef _MSC_VER
- /* sscanf warning (not currently scanning for strings) */
- # pragma warning(disable : 4996)
- #endif
- const uint32_t s_conservative_max_requests_in_flight = 8;
- const struct aws_byte_cursor g_application_xml_value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("application/xml");
- const struct aws_byte_cursor g_object_size_value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("ActualObjectSize");
- static void s_s3_meta_request_auto_ranged_get_destroy(struct aws_s3_meta_request *meta_request);
- static bool s_s3_auto_ranged_get_update(
- struct aws_s3_meta_request *meta_request,
- uint32_t flags,
- struct aws_s3_request **out_request);
- static int s_s3_auto_ranged_get_prepare_request(
- struct aws_s3_meta_request *meta_request,
- struct aws_s3_request *request);
- static void s_s3_auto_ranged_get_request_finished(
- struct aws_s3_meta_request *meta_request,
- struct aws_s3_request *request,
- int error_code);
- static struct aws_s3_meta_request_vtable s_s3_auto_ranged_get_vtable = {
- .update = s_s3_auto_ranged_get_update,
- .send_request_finish = aws_s3_meta_request_send_request_finish_default,
- .prepare_request = s_s3_auto_ranged_get_prepare_request,
- .init_signing_date_time = aws_s3_meta_request_init_signing_date_time_default,
- .sign_request = aws_s3_meta_request_sign_request_default,
- .finished_request = s_s3_auto_ranged_get_request_finished,
- .destroy = s_s3_meta_request_auto_ranged_get_destroy,
- .finish = aws_s3_meta_request_finish_default,
- };
- static int s_s3_auto_ranged_get_success_status(struct aws_s3_meta_request *meta_request) {
- AWS_PRECONDITION(meta_request);
- struct aws_s3_auto_ranged_get *auto_ranged_get = meta_request->impl;
- AWS_PRECONDITION(auto_ranged_get);
- if (auto_ranged_get->initial_message_has_range_header) {
- return AWS_S3_RESPONSE_STATUS_RANGE_SUCCESS;
- }
- return AWS_S3_RESPONSE_STATUS_SUCCESS;
- }
- /* Allocate a new auto-ranged-get meta request. */
- struct aws_s3_meta_request *aws_s3_meta_request_auto_ranged_get_new(
- struct aws_allocator *allocator,
- struct aws_s3_client *client,
- size_t part_size,
- const struct aws_s3_meta_request_options *options) {
- AWS_PRECONDITION(allocator);
- AWS_PRECONDITION(client);
- AWS_PRECONDITION(options);
- AWS_PRECONDITION(options->message);
- struct aws_s3_auto_ranged_get *auto_ranged_get =
- aws_mem_calloc(allocator, 1, sizeof(struct aws_s3_auto_ranged_get));
- /* Try to initialize the base type. */
- if (aws_s3_meta_request_init_base(
- allocator,
- client,
- part_size,
- false,
- options,
- auto_ranged_get,
- &s_s3_auto_ranged_get_vtable,
- &auto_ranged_get->base)) {
- AWS_LOGF_ERROR(
- AWS_LS_S3_META_REQUEST,
- "id=%p Could not initialize base type for Auto-Ranged-Get Meta Request.",
- (void *)auto_ranged_get);
- aws_mem_release(allocator, auto_ranged_get);
- return NULL;
- }
- struct aws_http_headers *headers = aws_http_message_get_headers(auto_ranged_get->base.initial_request_message);
- 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);
- return &auto_ranged_get->base;
- }
- static void s_s3_meta_request_auto_ranged_get_destroy(struct aws_s3_meta_request *meta_request) {
- AWS_PRECONDITION(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);
- }
- static bool s_s3_auto_ranged_get_update(
- struct aws_s3_meta_request *meta_request,
- uint32_t flags,
- struct aws_s3_request **out_request) {
- AWS_PRECONDITION(meta_request);
- AWS_PRECONDITION(out_request);
- struct aws_s3_auto_ranged_get *auto_ranged_get = meta_request->impl;
- struct aws_s3_request *request = NULL;
- bool work_remaining = false;
- /* BEGIN CRITICAL SECTION */
- {
- aws_s3_meta_request_lock_synced_data(meta_request);
- /* If nothing has set the "finish result" then this meta request is still in progress, and we can potentially
- * send additional requests. */
- if (!aws_s3_meta_request_has_finish_result_synced(meta_request)) {
- if ((flags & AWS_S3_META_REQUEST_UPDATE_FLAG_CONSERVATIVE) != 0) {
- uint32_t num_requests_in_flight =
- (auto_ranged_get->synced_data.num_parts_requested -
- auto_ranged_get->synced_data.num_parts_completed) +
- (uint32_t)aws_priority_queue_size(&meta_request->synced_data.pending_body_streaming_requests);
- /* auto-ranged-gets make use of body streaming, which will hold onto response bodies if parts earlier in
- * the file haven't arrived yet. This can potentially create a lot of backed up requests, causing us to
- * hit our global request limit. To help mitigate this, when the "conservative" flag is passed in, we
- * only allow the total amount of requests being sent/streamed to be inside of a set limit. */
- if (num_requests_in_flight > s_conservative_max_requests_in_flight) {
- goto has_work_remaining;
- }
- }
- /* If the overall range of the object that we are trying to retrieve isn't known yet, then we need to send a
- * request to figure that out. */
- if (!auto_ranged_get->synced_data.object_range_known) {
- /* If there exists a range header or we require validation of the response checksum, we currently always
- * do a head request first.
- * S3 returns the checksum of the entire object from the HEAD response
- *
- * For the range header value could be parsed client-side, doing so presents a number of
- * complications. For example, the given range could be an unsatisfiable range, and might not even
- * specify a complete range. To keep things simple, we are currently relying on the service to handle
- * turning the Range header into a Content-Range response header.*/
- bool head_object_required = auto_ranged_get->initial_message_has_range_header != 0 ||
- meta_request->checksum_config.validate_response_checksum;
- if (head_object_required) {
- /* If the head object request hasn't been sent yet, then send it now. */
- if (!auto_ranged_get->synced_data.head_object_sent) {
- request = aws_s3_request_new(
- meta_request,
- AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_HEAD_OBJECT,
- 0,
- AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS | AWS_S3_REQUEST_FLAG_PART_SIZE_RESPONSE_BODY);
- request->discovers_object_size = true;
- auto_ranged_get->synced_data.head_object_sent = true;
- }
- } else if (auto_ranged_get->synced_data.num_parts_requested == 0) {
- /* If we aren't using a head object, then discover the size of the object while trying to get the
- * first part. */
- request = aws_s3_request_new(
- meta_request,
- AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_PART,
- 1,
- AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS | AWS_S3_REQUEST_FLAG_PART_SIZE_RESPONSE_BODY);
- request->part_range_start = 0;
- request->part_range_end = meta_request->part_size - 1;
- request->discovers_object_size = true;
- ++auto_ranged_get->synced_data.num_parts_requested;
- }
- goto has_work_remaining;
- }
- /* If the object range is known and that range is empty, then we have an empty file to request. */
- if (auto_ranged_get->synced_data.object_range_start == 0 &&
- auto_ranged_get->synced_data.object_range_end == 0) {
- if (auto_ranged_get->synced_data.get_without_range_sent) {
- if (auto_ranged_get->synced_data.get_without_range_completed) {
- goto no_work_remaining;
- } else {
- goto has_work_remaining;
- }
- }
- request = aws_s3_request_new(
- meta_request,
- AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_INITIAL_MESSAGE,
- 0,
- AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS);
- auto_ranged_get->synced_data.get_without_range_sent = true;
- goto has_work_remaining;
- }
- /* If there are still more parts to be requested */
- if (auto_ranged_get->synced_data.num_parts_requested < auto_ranged_get->synced_data.total_num_parts) {
- if (meta_request->client->enable_read_backpressure) {
- /* Don't start a part until we have enough window to send bytes to the user.
- *
- * Note that we start a part once we have enough window to deliver ANY of its bytes.
- * If we waited until the window was large enough for the WHOLE part,
- * we could end up stuck in a situation where the user is
- * waiting for more bytes before they'll open the window,
- * and this implementation is waiting for more window before it will send more parts. */
- uint64_t read_data_requested =
- auto_ranged_get->synced_data.num_parts_requested * meta_request->part_size;
- if (read_data_requested >= meta_request->synced_data.read_window_running_total) {
- /* Avoid spamming users with this DEBUG message */
- if (auto_ranged_get->synced_data.read_window_warning_issued == 0) {
- auto_ranged_get->synced_data.read_window_warning_issued = 1;
- AWS_LOGF_DEBUG(
- AWS_LS_S3_META_REQUEST,
- "id=%p: Download paused because read window is zero. "
- "You must increment to window to continue.",
- (void *)meta_request);
- }
- goto has_work_remaining;
- }
- auto_ranged_get->synced_data.read_window_warning_issued = 0;
- }
- request = aws_s3_request_new(
- meta_request,
- AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_PART,
- auto_ranged_get->synced_data.num_parts_requested + 1,
- AWS_S3_REQUEST_FLAG_PART_SIZE_RESPONSE_BODY);
- aws_s3_get_part_range(
- auto_ranged_get->synced_data.object_range_start,
- auto_ranged_get->synced_data.object_range_end,
- meta_request->part_size,
- request->part_number,
- &request->part_range_start,
- &request->part_range_end);
- ++auto_ranged_get->synced_data.num_parts_requested;
- goto has_work_remaining;
- }
- /* If there are parts that have not attempted delivery to the caller, then there is still work being done.
- */
- if (meta_request->synced_data.num_parts_delivery_completed < auto_ranged_get->synced_data.total_num_parts) {
- goto has_work_remaining;
- }
- } else {
- /* Else, if there is a finish result set, make sure that all work-in-progress winds down before the meta
- * request completely exits. */
- if (auto_ranged_get->synced_data.head_object_sent && !auto_ranged_get->synced_data.head_object_completed) {
- goto has_work_remaining;
- }
- /* Wait for all requests to complete (successfully or unsuccessfully) before finishing.*/
- if (auto_ranged_get->synced_data.num_parts_completed < auto_ranged_get->synced_data.num_parts_requested) {
- goto has_work_remaining;
- }
- if (auto_ranged_get->synced_data.get_without_range_sent &&
- !auto_ranged_get->synced_data.get_without_range_completed) {
- goto has_work_remaining;
- }
- /* If some parts are still being delivered to the caller, then wait for those to finish. */
- if (meta_request->synced_data.num_parts_delivery_completed <
- meta_request->synced_data.num_parts_delivery_sent) {
- goto has_work_remaining;
- }
- }
- goto no_work_remaining;
- has_work_remaining:
- work_remaining = true;
- if (request != NULL) {
- AWS_LOGF_DEBUG(
- AWS_LS_S3_META_REQUEST,
- "id=%p: Returning request %p for part %d of %d",
- (void *)meta_request,
- (void *)request,
- request->part_number,
- auto_ranged_get->synced_data.total_num_parts);
- }
- no_work_remaining:
- if (!work_remaining) {
- aws_s3_meta_request_set_success_synced(meta_request, s_s3_auto_ranged_get_success_status(meta_request));
- if (auto_ranged_get->synced_data.num_parts_checksum_validated ==
- auto_ranged_get->synced_data.num_parts_requested) {
- /* If we have validated the checksum for every parts, we set the meta request level checksum validation
- * result.*/
- meta_request->synced_data.finish_result.did_validate = true;
- meta_request->synced_data.finish_result.validation_algorithm = auto_ranged_get->validation_algorithm;
- }
- }
- aws_s3_meta_request_unlock_synced_data(meta_request);
- }
- /* END CRITICAL SECTION */
- if (work_remaining) {
- *out_request = request;
- } else {
- AWS_ASSERT(request == NULL);
- aws_s3_meta_request_finish(meta_request);
- }
- return work_remaining;
- }
- /* Given a request, prepare it for sending based on its description. */
- static int s_s3_auto_ranged_get_prepare_request(
- struct aws_s3_meta_request *meta_request,
- struct aws_s3_request *request) {
- AWS_PRECONDITION(meta_request);
- AWS_PRECONDITION(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:
- /* A head object will be a copy of the original headers but with a HEAD request method. */
- message = aws_s3_message_util_copy_http_message_no_body_all_headers(
- meta_request->allocator, meta_request->initial_request_message);
- if (message) {
- aws_http_message_set_request_method(message, g_head_method);
- }
- break;
- case AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_PART:
- message = aws_s3_ranged_get_object_message_new(
- meta_request->allocator,
- meta_request->initial_request_message,
- request->part_range_start,
- request->part_range_end);
- break;
- case AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_INITIAL_MESSAGE:
- message = aws_s3_message_util_copy_http_message_no_body_all_headers(
- meta_request->allocator, meta_request->initial_request_message);
- break;
- }
- if (message == NULL) {
- AWS_LOGF_ERROR(
- AWS_LS_S3_META_REQUEST,
- "id=%p Could not create message for request with tag %d for auto-ranged-get meta request.",
- (void *)meta_request,
- request->request_tag);
- goto message_alloc_failed;
- }
- 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);
- AWS_LOGF_DEBUG(
- AWS_LS_S3_META_REQUEST,
- "id=%p: Created request %p for part %d",
- (void *)meta_request,
- (void *)request,
- request->part_number);
- return AWS_OP_SUCCESS;
- message_alloc_failed:
- return AWS_OP_ERR;
- }
- /* Check the finish result of meta request, in case of the request failed because of downloading an empty file */
- static bool s_check_empty_file_download_error(struct aws_s3_request *failed_request) {
- struct aws_http_headers *failed_headers = failed_request->send_data.response_headers;
- struct aws_byte_buf failed_body = failed_request->send_data.response_body;
- if (failed_headers && failed_body.capacity > 0) {
- struct aws_byte_cursor content_type;
- AWS_ZERO_STRUCT(content_type);
- if (!aws_http_headers_get(failed_headers, g_content_type_header_name, &content_type)) {
- /* Content type found */
- if (aws_byte_cursor_eq_ignore_case(&content_type, &g_application_xml_value)) {
- /* XML response */
- struct aws_byte_cursor body_cursor = aws_byte_cursor_from_buf(&failed_body);
- struct aws_string *size =
- aws_xml_get_top_level_tag(failed_request->allocator, &g_object_size_value, &body_cursor);
- bool check_size = aws_string_eq_c_str(size, "0");
- aws_string_destroy(size);
- if (check_size) {
- return true;
- }
- }
- }
- }
- return false;
- }
- static int s_discover_object_range_and_content_length(
- struct aws_s3_meta_request *meta_request,
- struct aws_s3_request *request,
- int error_code,
- uint64_t *out_total_content_length,
- uint64_t *out_object_range_start,
- uint64_t *out_object_range_end) {
- AWS_PRECONDITION(out_total_content_length);
- AWS_PRECONDITION(out_object_range_start);
- AWS_PRECONDITION(out_object_range_end);
- int result = AWS_OP_ERR;
- uint64_t total_content_length = 0;
- uint64_t object_range_start = 0;
- uint64_t object_range_end = 0;
- AWS_ASSERT(request->discovers_object_size);
- 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:
- if (error_code != AWS_ERROR_SUCCESS) {
- /* If the head request failed, there's nothing we can do, so resurface the error code. */
- aws_raise_error(error_code);
- break;
- }
- /* There should be a Content-Length header that indicates the total size of the range.*/
- if (aws_s3_parse_content_length_response_header(
- meta_request->allocator, request->send_data.response_headers, &total_content_length)) {
- AWS_LOGF_ERROR(
- AWS_LS_S3_META_REQUEST,
- "id=%p Could not find content-length header for request %p",
- (void *)meta_request,
- (void *)request);
- break;
- }
- /* if the inital message had a ranged header, there should also be a Content-Range header that specifies the
- * object range and total object size. Otherwise the size and range should be equal to the
- * total_content_length. */
- if (!auto_ranged_get->initial_message_has_range_header) {
- object_range_end = total_content_length - 1;
- } else if (aws_s3_parse_content_range_response_header(
- meta_request->allocator,
- request->send_data.response_headers,
- &object_range_start,
- &object_range_end,
- NULL)) {
- AWS_LOGF_ERROR(
- AWS_LS_S3_META_REQUEST,
- "id=%p Could not find content-range header for request %p",
- (void *)meta_request,
- (void *)request);
- break;
- }
- result = AWS_OP_SUCCESS;
- break;
- case AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_PART:
- AWS_ASSERT(request->part_number == 1);
- if (error_code != AWS_ERROR_SUCCESS) {
- /* If we hit an empty file while trying to discover the object-size via part, then this request
- failure
- * is as designed. */
- if (s_check_empty_file_download_error(request)) {
- AWS_LOGF_DEBUG(
- AWS_LS_S3_META_REQUEST,
- "id=%p Detected empty file with request %p. Sending new request without range header.",
- (void *)meta_request,
- (void *)request);
- total_content_length = 0ULL;
- result = AWS_OP_SUCCESS;
- } else {
- /* Otherwise, resurface the error code. */
- aws_raise_error(error_code);
- }
- break;
- }
- AWS_ASSERT(request->send_data.response_headers != NULL);
- /* Parse the object size from the part response. */
- if (aws_s3_parse_content_range_response_header(
- meta_request->allocator, request->send_data.response_headers, NULL, NULL, &total_content_length)) {
- AWS_LOGF_ERROR(
- AWS_LS_S3_META_REQUEST,
- "id=%p Could not find content-range header for request %p",
- (void *)meta_request,
- (void *)request);
- break;
- }
- /* When discovering the object size via first-part, the object range is the entire object. */
- object_range_start = 0;
- object_range_end = total_content_length - 1;
- result = AWS_OP_SUCCESS;
- break;
- default:
- AWS_ASSERT(false);
- break;
- }
- if (result == AWS_OP_SUCCESS) {
- *out_total_content_length = total_content_length;
- *out_object_range_start = object_range_start;
- *out_object_range_end = object_range_end;
- }
- return result;
- }
- static void s_s3_auto_ranged_get_request_finished(
- struct aws_s3_meta_request *meta_request,
- struct aws_s3_request *request,
- int error_code) {
- AWS_PRECONDITION(meta_request);
- AWS_PRECONDITION(meta_request->impl);
- AWS_PRECONDITION(request);
- struct aws_s3_auto_ranged_get *auto_ranged_get = meta_request->impl;
- AWS_PRECONDITION(auto_ranged_get);
- uint64_t total_content_length = 0ULL;
- uint64_t object_range_start = 0ULL;
- uint64_t object_range_end = 0ULL;
- bool found_object_size = false;
- bool request_failed = error_code != AWS_ERROR_SUCCESS;
- if (request->discovers_object_size) {
- /* Try to discover the object-range and content length.*/
- if (s_discover_object_range_and_content_length(
- meta_request, request, error_code, &total_content_length, &object_range_start, &object_range_end)) {
- error_code = aws_last_error_or_unknown();
- 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;
- found_object_size = true;
- if (meta_request->headers_callback != NULL) {
- struct aws_http_headers *response_headers = aws_http_headers_new(meta_request->allocator);
- copy_http_headers(request->send_data.response_headers, response_headers);
- /* If this request is a part, then the content range isn't applicable. */
- if (request->request_tag == AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_PART) {
- /* For now, we can assume that discovery of size via the first part of the object does not apply to
- * breaking up a ranged request. If it ever does, then we will need to repopulate this header. */
- AWS_ASSERT(!auto_ranged_get->initial_message_has_range_header);
- aws_http_headers_erase(response_headers, g_content_range_header_name);
- }
- char content_length_buffer[64] = "";
- snprintf(content_length_buffer, sizeof(content_length_buffer), "%" PRIu64, total_content_length);
- aws_http_headers_set(
- response_headers, g_content_length_header_name, aws_byte_cursor_from_c_str(content_length_buffer));
- if (meta_request->headers_callback(
- meta_request,
- response_headers,
- s_s3_auto_ranged_get_success_status(meta_request),
- meta_request->user_data)) {
- error_code = aws_last_error_or_unknown();
- }
- meta_request->headers_callback = NULL;
- aws_http_headers_release(response_headers);
- }
- }
- update_synced_data:
- /* BEGIN CRITICAL SECTION */
- {
- aws_s3_meta_request_lock_synced_data(meta_request);
- /* If the object range was found, then record it. */
- if (found_object_size) {
- AWS_ASSERT(!auto_ranged_get->synced_data.object_range_known);
- auto_ranged_get->synced_data.object_range_known = true;
- auto_ranged_get->synced_data.object_range_start = object_range_start;
- auto_ranged_get->synced_data.object_range_end = object_range_end;
- auto_ranged_get->synced_data.total_num_parts =
- aws_s3_get_num_parts(meta_request->part_size, object_range_start, object_range_end);
- }
- switch (request->request_tag) {
- case AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_HEAD_OBJECT:
- auto_ranged_get->synced_data.head_object_completed = true;
- AWS_LOGF_DEBUG(AWS_LS_S3_META_REQUEST, "id=%p Head object completed.", (void *)meta_request);
- break;
- case AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_PART:
- ++auto_ranged_get->synced_data.num_parts_completed;
- if (!request_failed) {
- /* Record the number of parts that checksum has been validated */
- if (request->did_validate) {
- if (auto_ranged_get->validation_algorithm == AWS_SCA_NONE) {
- auto_ranged_get->validation_algorithm = request->validation_algorithm;
- }
- /* They should be the same. */
- AWS_ASSERT(auto_ranged_get->validation_algorithm == request->validation_algorithm);
- ++auto_ranged_get->synced_data.num_parts_checksum_validated;
- }
- ++auto_ranged_get->synced_data.num_parts_successful;
- aws_s3_meta_request_stream_response_body_synced(meta_request, request);
- AWS_LOGF_DEBUG(
- AWS_LS_S3_META_REQUEST,
- "id=%p: %d out of %d parts have completed.",
- (void *)meta_request,
- (auto_ranged_get->synced_data.num_parts_successful +
- auto_ranged_get->synced_data.num_parts_failed),
- auto_ranged_get->synced_data.total_num_parts);
- } else {
- ++auto_ranged_get->synced_data.num_parts_failed;
- }
- break;
- case AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_INITIAL_MESSAGE:
- AWS_LOGF_DEBUG(
- AWS_LS_S3_META_REQUEST, "id=%p Get of file using initial message completed.", (void *)meta_request);
- auto_ranged_get->synced_data.get_without_range_completed = true;
- break;
- }
- 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
- */
- meta_request->synced_data.finish_result.did_validate = true;
- meta_request->synced_data.finish_result.validation_algorithm = request->validation_algorithm;
- }
- }
- aws_s3_meta_request_unlock_synced_data(meta_request);
- }
- /* END CRITICAL SECTION */
- }
|