in source/s3_auto_ranged_get.c [146:322]
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;
aws_s3_meta_request_lock_synced_data(meta_request);
/* If nothing has set the 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, we currently always do a head request first. While 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;
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,
1,
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 (auto_ranged_get->synced_data.num_parts_requested < auto_ranged_get->synced_data.total_num_parts) {
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));
}
aws_s3_meta_request_unlock_synced_data(meta_request);
if (work_remaining) {
*out_request = request;
} else {
AWS_ASSERT(request == NULL);
aws_s3_meta_request_finish(meta_request);
}
return work_remaining;
}