static bool s_s3_copy_object_update()

in source/s3_copy_object.c [137:353]


static bool s_s3_copy_object_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_request *request = NULL;
    bool work_remaining = false;

    struct aws_s3_copy_object *copy_object = meta_request->impl;

    aws_s3_meta_request_lock_synced_data(meta_request);

    if (!aws_s3_meta_request_has_finish_result_synced(meta_request)) {

        /* If we haven't already sent the GetObject HEAD request to get the source object size, do so now. */
        if (!copy_object->synced_data.head_object_sent) {
            request = aws_s3_request_new(
                meta_request,
                AWS_S3_COPY_OBJECT_REQUEST_TAG_GET_OBJECT_SIZE,
                0,
                AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS);

            copy_object->synced_data.head_object_sent = true;

            goto has_work_remaining;
        }

        if (!copy_object->synced_data.head_object_completed) {
            /* we have not received the object size response yet */
            goto has_work_remaining;
        }

        if (copy_object->synced_data.content_length < s_multipart_copy_minimum_object_size) {
            /* object is too small to use multipart upload: forwards the original CopyObject request to S3 instead. */
            if (!copy_object->synced_data.copy_request_bypass_sent) {
                request = aws_s3_request_new(
                    meta_request,
                    AWS_S3_COPY_OBJECT_REQUEST_TAG_BYPASS,
                    1,
                    AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS);

                AWS_LOGF_DEBUG(
                    AWS_LS_S3_META_REQUEST,
                    "id=%p: Meta Request CopyObject created bypass request %p",
                    (void *)meta_request,
                    (void *)request);

                copy_object->synced_data.copy_request_bypass_sent = true;
                goto has_work_remaining;
            }

            /* If the bypass request hasn't been completed, then wait for it to be completed. */
            if (!copy_object->synced_data.copy_request_bypass_completed) {
                goto has_work_remaining;
            } else {
                goto no_work_remaining;
            }
        }

        /* Object size is large enough to use multipart copy. If we haven't already sent a create-multipart-upload
         * message, do so now. */
        if (!copy_object->synced_data.create_multipart_upload_sent) {
            request = aws_s3_request_new(
                meta_request,
                AWS_S3_COPY_OBJECT_REQUEST_TAG_CREATE_MULTIPART_UPLOAD,
                0,
                AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS);

            copy_object->synced_data.create_multipart_upload_sent = true;
            goto has_work_remaining;
        }

        /* If the create-multipart-upload message hasn't been completed, then there is still additional work to do, but
         * it can't be done yet. */
        if (!copy_object->synced_data.create_multipart_upload_completed) {
            goto has_work_remaining;
        }

        /* If we haven't sent all of the parts yet, then set up to send a new part now. */
        if (copy_object->synced_data.num_parts_sent < copy_object->synced_data.total_num_parts) {

            if ((flags & AWS_S3_META_REQUEST_UPDATE_FLAG_CONSERVATIVE) != 0) {
                uint32_t num_parts_in_flight =
                    (copy_object->synced_data.num_parts_sent - copy_object->synced_data.num_parts_completed);

                /* TODO: benchmark if there is need to limit the amount of upload part copy in flight requests */
                if (num_parts_in_flight > 0) {
                    goto has_work_remaining;
                }
            }

            /* Allocate a request for another part. */
            request = aws_s3_request_new(
                meta_request,
                AWS_S3_COPY_OBJECT_REQUEST_TAG_MULTIPART_COPY,
                0,
                AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS);

            request->part_number = copy_object->threaded_update_data.next_part_number;

            ++copy_object->threaded_update_data.next_part_number;
            ++copy_object->synced_data.num_parts_sent;

            AWS_LOGF_DEBUG(
                AWS_LS_S3_META_REQUEST,
                "id=%p: Returning request %p for part %d",
                (void *)meta_request,
                (void *)request,
                request->part_number);

            goto has_work_remaining;
        }

        /* There is one more request to send after all of the parts (the complete-multipart-upload) but it can't be done
         * until all of the parts have been completed.*/
        if (copy_object->synced_data.num_parts_completed != copy_object->synced_data.total_num_parts) {
            goto has_work_remaining;
        }

        /* If the complete-multipart-upload request hasn't been set yet, then send it now. */
        if (!copy_object->synced_data.complete_multipart_upload_sent) {
            request = aws_s3_request_new(
                meta_request,
                AWS_S3_COPY_OBJECT_REQUEST_TAG_COMPLETE_MULTIPART_UPLOAD,
                0,
                AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS);

            copy_object->synced_data.complete_multipart_upload_sent = true;
            goto has_work_remaining;
        }

        /* Wait for the complete-multipart-upload request to finish. */
        if (!copy_object->synced_data.complete_multipart_upload_completed) {
            goto has_work_remaining;
        }

        goto no_work_remaining;
    } else {

        /* If the create multipart upload hasn't been sent, then there is nothing left to do when canceling. */
        if (!copy_object->synced_data.create_multipart_upload_sent) {
            goto no_work_remaining;
        }

        /* If the create-multipart-upload request is still in flight, wait for it to finish. */
        if (!copy_object->synced_data.create_multipart_upload_completed) {
            goto has_work_remaining;
        }

        /* If the number of parts completed is less than the number of parts sent, then we need to wait until all of
         * those parts are done sending before aborting. */
        if (copy_object->synced_data.num_parts_completed < copy_object->synced_data.num_parts_sent) {
            goto has_work_remaining;
        }

        /* If the complete-multipart-upload is already in flight, then we can't necessarily send an abort. */
        if (copy_object->synced_data.complete_multipart_upload_sent &&
            !copy_object->synced_data.complete_multipart_upload_completed) {
            goto has_work_remaining;
        }

        /* If the complete-multipart-upload completed successfully, then there is nothing to abort since the transfer
         * has already finished. */
        if (copy_object->synced_data.complete_multipart_upload_completed &&
            copy_object->synced_data.complete_multipart_upload_error_code == AWS_ERROR_SUCCESS) {
            goto no_work_remaining;
        }

        /* If we made it here, and the abort-multipart-upload message hasn't been sent yet, then do so now. */
        if (!copy_object->synced_data.abort_multipart_upload_sent) {
            if (copy_object->upload_id == NULL) {
                goto no_work_remaining;
            }

            request = aws_s3_request_new(
                meta_request,
                AWS_S3_COPY_OBJECT_REQUEST_TAG_ABORT_MULTIPART_UPLOAD,
                0,
                AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS | AWS_S3_REQUEST_FLAG_ALWAYS_SEND);

            copy_object->synced_data.abort_multipart_upload_sent = true;

            goto has_work_remaining;
        }

        /* Wait for the multipart upload to be completed. */
        if (!copy_object->synced_data.abort_multipart_upload_completed) {
            goto has_work_remaining;
        }

        goto no_work_remaining;
    }

has_work_remaining:
    work_remaining = true;

no_work_remaining:

    if (!work_remaining) {
        aws_s3_meta_request_set_success_synced(meta_request, AWS_S3_RESPONSE_STATUS_SUCCESS);
    }

    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;
}