diff --git a/include/aws/s3/private/s3_default_meta_request.h b/include/aws/s3/private/s3_default_meta_request.h index 123c963b5..4ce93f91a 100644 --- a/include/aws/s3/private/s3_default_meta_request.h +++ b/include/aws/s3/private/s3_default_meta_request.h @@ -15,6 +15,12 @@ struct aws_s3_meta_request_default { size_t content_length; + /* Actual type for the single request (may be AWS_S3_REQUEST_TYPE_UNKNOWN) */ + enum aws_s3_request_type request_type; + + /* S3 operation name for the single request (NULL if unknown) */ + struct aws_string *operation_name; + /* Members to only be used when the mutex in the base type is locked. */ struct { int cached_response_status; @@ -30,6 +36,7 @@ struct aws_s3_meta_request_default { struct aws_s3_meta_request *aws_s3_meta_request_default_new( struct aws_allocator *allocator, struct aws_s3_client *client, + enum aws_s3_request_type request_type, uint64_t content_length, bool should_compute_content_md5, const struct aws_s3_meta_request_options *options); diff --git a/include/aws/s3/private/s3_meta_request_impl.h b/include/aws/s3/private/s3_meta_request_impl.h index 93ebb191a..2a44606dd 100644 --- a/include/aws/s3/private/s3_meta_request_impl.h +++ b/include/aws/s3/private/s3_meta_request_impl.h @@ -117,9 +117,6 @@ struct aws_s3_meta_request_vtable { /* Pause the given request */ int (*pause)(struct aws_s3_meta_request *meta_request, struct aws_s3_meta_request_resume_token **resume_token); - - /* Get the type of the aws_s3_request */ - int (*get_request_type)(const struct aws_s3_request *request); }; /** diff --git a/include/aws/s3/private/s3_request.h b/include/aws/s3/private/s3_request.h index ae00a9f18..9d59558b0 100644 --- a/include/aws/s3/private/s3_request.h +++ b/include/aws/s3/private/s3_request.h @@ -75,6 +75,8 @@ struct aws_s3_request_metrics { struct aws_string *host_address; /* The the request ID header value. */ struct aws_string *request_id; + /* S3 operation name for the request (NULL if unknown) */ + struct aws_string *operation_name; /* The type of request made */ enum aws_s3_request_type request_type; } req_resp_info_metrics; @@ -155,9 +157,18 @@ struct aws_s3_request { * by the derived type. Request tags do not necessarily map 1:1 with actual S3 API requests. (For example, they can * be more contextual, like "first part" instead of just "part".) */ - /* TODO: this should be a union type to make it clear that this could be one of two enums for puts, and gets. */ + /* TODO: Eliminate the concept of "request tag" and just use request_type. + * It's confusing having 2 concepts that are so similar. + * There's only 1 case where 2 tags used the same type, + * we can use some other bool/flag to differentiate this 1 case. */ int request_tag; + /* Actual S3 type for the single request (may be AWS_S3_REQUEST_TYPE_UNKNOWN) */ + enum aws_s3_request_type request_type; + + /* S3 operation name for the single request (e.g. "CompleteMultipartUpload") (NULL if unknown) */ + struct aws_string *operation_name; + /* Members of this structure will be repopulated each time the request is sent. If the request fails, and needs to * be retried, then the members of this structure will be cleaned up and re-populated on the next send. */ @@ -224,6 +235,7 @@ AWS_S3_API struct aws_s3_request *aws_s3_request_new( struct aws_s3_meta_request *meta_request, int request_tag, + enum aws_s3_request_type request_type, uint32_t part_number, uint32_t flags); @@ -245,7 +257,8 @@ struct aws_s3_request *aws_s3_request_release(struct aws_s3_request *request); AWS_S3_API struct aws_s3_request_metrics *aws_s3_request_metrics_new( struct aws_allocator *allocator, - struct aws_http_message *message); + const struct aws_s3_request *request, + const struct aws_http_message *message); AWS_EXTERN_C_END diff --git a/include/aws/s3/s3_client.h b/include/aws/s3/s3_client.h index 71f95d75d..b9e7e1677 100644 --- a/include/aws/s3/s3_client.h +++ b/include/aws/s3/s3_client.h @@ -85,11 +85,21 @@ enum aws_s3_meta_request_type { }; /** - * The type of S3 request made. Used by metrics. + * The type of a single S3 HTTP request. Used by metrics. + * A meta-request can make multiple S3 HTTP requests under the hood. + * + * For example, AWS_S3_META_REQUEST_TYPE_PUT_OBJECT for a large file will + * do multipart upload, resulting in 3+ HTTP requests: + * AWS_S3_REQUEST_TYPE_CREATE_MULTIPART_UPLOAD, one or more AWS_S3_REQUEST_TYPE_UPLOAD_PART, + * and finally AWS_S3_REQUEST_TYPE_COMPLETE_MULTIPART_UPLOAD. + * + * aws_s3_request_type_operation_name() returns the S3 operation name + * for types that map (e.g. AWS_S3_REQUEST_TYPE_HEAD_OBJECT -> "HeadObject"), + * or empty string for types that don't map (e.g. AWS_S3_REQUEST_TYPE_UNKNOWN -> ""). */ enum aws_s3_request_type { - /* Same as the original HTTP request passed to aws_s3_meta_request_options */ - AWS_S3_REQUEST_TYPE_DEFAULT, + /* The actual type of the single S3 HTTP request is unknown */ + AWS_S3_REQUEST_TYPE_UNKNOWN, /* S3 APIs */ AWS_S3_REQUEST_TYPE_HEAD_OBJECT, @@ -100,8 +110,14 @@ enum aws_s3_request_type { AWS_S3_REQUEST_TYPE_ABORT_MULTIPART_UPLOAD, AWS_S3_REQUEST_TYPE_COMPLETE_MULTIPART_UPLOAD, AWS_S3_REQUEST_TYPE_UPLOAD_PART_COPY, + AWS_S3_REQUEST_TYPE_COPY_OBJECT, + AWS_S3_REQUEST_TYPE_PUT_OBJECT, + /* Max enum value */ AWS_S3_REQUEST_TYPE_MAX, + + /** @deprecated Use AWS_S3_REQUEST_TYPE_UNKNOWN if the actual S3 HTTP request type is unknown */ + AWS_S3_REQUEST_TYPE_DEFAULT = AWS_S3_REQUEST_TYPE_UNKNOWN, }; /** @@ -481,6 +497,15 @@ struct aws_s3_meta_request_options { /* The type of meta request we will be trying to accelerate. */ enum aws_s3_meta_request_type type; + /** + * Optional. + * The S3 operation name (e.g. "CreateBucket"). + * This will only be used when type is AWS_S3_META_REQUEST_TYPE_DEFAULT; + * it is automatically populated for other meta-request types. + * This name is used to fill out details in metrics and error reports. + */ + struct aws_byte_cursor operation_name; + /* Signing options to be used for each request created for this meta request. If NULL, options in the client will * be used. If not NULL, these options will override the client options. */ const struct aws_signing_config_aws *signing_config; @@ -602,12 +627,23 @@ struct aws_s3_meta_request_options { */ struct aws_s3_meta_request_result { - /* HTTP Headers for the failed request that triggered finish of the meta request. NULL if no request failed. */ + /* If meta request failed due to an HTTP error response from S3, these are the headers. + * NULL if meta request failed for another reason. */ struct aws_http_headers *error_response_headers; - /* Response body for the failed request that triggered finishing of the meta request. NULL if no request failed.*/ + /* If meta request failed due to an HTTP error response from S3, this the body. + * NULL if meta request failed for another reason, or if the response had no body (such as a HEAD response). */ struct aws_byte_buf *error_response_body; + /* If meta request failed due to an HTTP error response from S3, + * this is the name of the S3 operation it was responding to. + * For example, if a AWS_S3_META_REQUEST_TYPE_PUT_OBJECT fails this could be + * "PutObject, "CreateMultipartUpload", "UploadPart", "CompleteMultipartUpload", or others. + * For AWS_S3_META_REQUEST_TYPE_DEFAULT, this is the same value passed to + * aws_s3_meta_request_options.operation_name. + * NULL if the meta request failed for another reason, or the operation name is not known. */ + struct aws_string *error_response_operation_name; + /* Response status of the failed request or of the entire meta request. */ int response_status; @@ -823,6 +859,17 @@ void aws_s3_init_default_signing_config( const struct aws_byte_cursor region, struct aws_credentials_provider *credentials_provider); +/** + * Return operation name for aws_s3_request_type, + * or empty string if the type doesn't map to an actual operation. + * For example: + * AWS_S3_REQUEST_TYPE_HEAD_OBJECT -> "HeadObject" + * AWS_S3_REQUEST_TYPE_UNKNOWN -> "" + * AWS_S3_REQUEST_TYPE_MAX -> "" + */ +AWS_S3_API +const char *aws_s3_request_type_operation_name(enum aws_s3_request_type type); + /** * Add a reference, keeping this object alive. * The reference must be released when you are done with it, or it's memory will never be cleaned up. @@ -974,7 +1021,19 @@ int aws_s3_request_metrics_get_thread_id(const struct aws_s3_request_metrics *me AWS_S3_API int aws_s3_request_metrics_get_request_stream_id(const struct aws_s3_request_metrics *metrics, uint32_t *out_stream_id); -/* Get the request type from request metrics. */ +/** + * Get the S3 operation name of the request (e.g. "HeadObject"). + * If unavailable, AWS_ERROR_S3_METRIC_DATA_NOT_AVAILABLE will be raised. + * If available, out_operation_name will be set to a string. + * Be warned this string's lifetime is tied to the metrics object. + */ +AWS_S3_API +int aws_s3_request_metrics_get_operation_name( + const struct aws_s3_request_metrics *metrics, + const struct aws_string **out_operation_name); + +/* Get the request type from request metrics. + * If you just need a string, aws_s3_request_metrics_get_operation_name() is more reliable. */ AWS_S3_API void aws_s3_request_metrics_get_request_type( const struct aws_s3_request_metrics *metrics, diff --git a/source/s3_auto_ranged_get.c b/source/s3_auto_ranged_get.c index e338d7f4a..5e72b3a74 100644 --- a/source/s3_auto_ranged_get.c +++ b/source/s3_auto_ranged_get.c @@ -28,8 +28,6 @@ static void s_s3_auto_ranged_get_request_finished( struct aws_s3_request *request, int error_code); -static int s_s3_auto_ranged_get_request_type(const struct aws_s3_request *request); - 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, @@ -39,21 +37,8 @@ static struct aws_s3_meta_request_vtable s_s3_auto_ranged_get_vtable = { .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, - .get_request_type = s_s3_auto_ranged_get_request_type, }; -static int s_s3_auto_ranged_get_request_type(const struct aws_s3_request *request) { - switch (request->request_tag) { - case AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_HEAD_OBJECT: - return AWS_S3_REQUEST_TYPE_HEAD_OBJECT; - case AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_PART: - case AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_INITIAL_MESSAGE: - return AWS_S3_REQUEST_TYPE_GET_OBJECT; - } - AWS_ASSERT(false); - return AWS_S3_REQUEST_TYPE_MAX; -} - static int s_s3_auto_ranged_get_success_status(struct aws_s3_meta_request *meta_request) { AWS_PRECONDITION(meta_request); @@ -176,7 +161,8 @@ static bool s_s3_auto_ranged_get_update( request = aws_s3_request_new( meta_request, AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_HEAD_OBJECT, - 0, + AWS_S3_REQUEST_TYPE_HEAD_OBJECT, + 0 /*part_number*/, AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS); request->discovers_object_size = true; @@ -197,7 +183,8 @@ static bool s_s3_auto_ranged_get_update( request = aws_s3_request_new( meta_request, AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_PART, - 1, + AWS_S3_REQUEST_TYPE_GET_OBJECT, + 1 /*part_number*/, AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS | AWS_S3_REQUEST_FLAG_PART_SIZE_RESPONSE_BODY); request->ticket = ticket; @@ -223,7 +210,8 @@ static bool s_s3_auto_ranged_get_update( request = aws_s3_request_new( meta_request, AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_INITIAL_MESSAGE, - 0, + AWS_S3_REQUEST_TYPE_GET_OBJECT, + 0 /*part_number*/, AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS); auto_ranged_get->synced_data.get_without_range_sent = true; @@ -272,7 +260,8 @@ static bool s_s3_auto_ranged_get_update( 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_TYPE_GET_OBJECT, + auto_ranged_get->synced_data.num_parts_requested + 1 /*part_number*/, AWS_S3_REQUEST_FLAG_PART_SIZE_RESPONSE_BODY); request->ticket = ticket; diff --git a/source/s3_auto_ranged_put.c b/source/s3_auto_ranged_put.c index adcfb086f..c77cbce5f 100644 --- a/source/s3_auto_ranged_put.c +++ b/source/s3_auto_ranged_put.c @@ -294,23 +294,6 @@ static int s_try_init_resume_state_from_persisted_data( return AWS_OP_SUCCESS; } -static int s_s3_auto_ranged_put_request_type(const struct aws_s3_request *request) { - switch (request->request_tag) { - case AWS_S3_AUTO_RANGED_PUT_REQUEST_TAG_LIST_PARTS: - return AWS_S3_REQUEST_TYPE_LIST_PARTS; - case AWS_S3_AUTO_RANGED_PUT_REQUEST_TAG_CREATE_MULTIPART_UPLOAD: - return AWS_S3_REQUEST_TYPE_CREATE_MULTIPART_UPLOAD; - case AWS_S3_AUTO_RANGED_PUT_REQUEST_TAG_PART: - return AWS_S3_REQUEST_TYPE_UPLOAD_PART; - case AWS_S3_AUTO_RANGED_PUT_REQUEST_TAG_ABORT_MULTIPART_UPLOAD: - return AWS_S3_REQUEST_TYPE_ABORT_MULTIPART_UPLOAD; - case AWS_S3_AUTO_RANGED_PUT_REQUEST_TAG_COMPLETE_MULTIPART_UPLOAD: - return AWS_S3_REQUEST_TYPE_COMPLETE_MULTIPART_UPLOAD; - } - AWS_ASSERT(false); - return AWS_S3_REQUEST_TYPE_MAX; -} - static struct aws_s3_meta_request_vtable s_s3_auto_ranged_put_vtable = { .update = s_s3_auto_ranged_put_update, .send_request_finish = s_s3_auto_ranged_put_send_request_finish, @@ -321,7 +304,6 @@ static struct aws_s3_meta_request_vtable s_s3_auto_ranged_put_vtable = { .destroy = s_s3_meta_request_auto_ranged_put_destroy, .finish = aws_s3_meta_request_finish_default, .pause = s_s3_auto_ranged_put_pause, - .get_request_type = s_s3_auto_ranged_put_request_type, }; /* Allocate a new auto-ranged put meta request */ @@ -480,7 +462,8 @@ static bool s_s3_auto_ranged_put_update( request = aws_s3_request_new( meta_request, AWS_S3_AUTO_RANGED_PUT_REQUEST_TAG_LIST_PARTS, - 0, + AWS_S3_REQUEST_TYPE_LIST_PARTS, + 0 /*part_number*/, AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS); auto_ranged_put->synced_data.list_parts_state.started = true; @@ -494,7 +477,8 @@ static bool s_s3_auto_ranged_put_update( request = aws_s3_request_new( meta_request, AWS_S3_AUTO_RANGED_PUT_REQUEST_TAG_LIST_PARTS, - 0, + AWS_S3_REQUEST_TYPE_LIST_PARTS, + 0 /*part_number*/, AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS); auto_ranged_put->synced_data.list_parts_state.continues = false; goto has_work_remaining; @@ -510,7 +494,8 @@ static bool s_s3_auto_ranged_put_update( request = aws_s3_request_new( meta_request, AWS_S3_AUTO_RANGED_PUT_REQUEST_TAG_CREATE_MULTIPART_UPLOAD, - 0, + AWS_S3_REQUEST_TYPE_CREATE_MULTIPART_UPLOAD, + 0 /*part_number*/, AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS); auto_ranged_put->synced_data.create_multipart_upload_sent = true; @@ -564,7 +549,8 @@ static bool s_s3_auto_ranged_put_update( request = aws_s3_request_new( meta_request, AWS_S3_AUTO_RANGED_PUT_REQUEST_TAG_PART, - 0, + AWS_S3_REQUEST_TYPE_UPLOAD_PART, + 0 /*part_number*/, AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS | AWS_S3_REQUEST_FLAG_PART_SIZE_REQUEST_BODY); request->part_number = auto_ranged_put->threaded_update_data.next_part_number; @@ -610,7 +596,8 @@ static bool s_s3_auto_ranged_put_update( request = aws_s3_request_new( meta_request, AWS_S3_AUTO_RANGED_PUT_REQUEST_TAG_COMPLETE_MULTIPART_UPLOAD, - 0, + AWS_S3_REQUEST_TYPE_COMPLETE_MULTIPART_UPLOAD, + 0 /*part_number*/, AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS); auto_ranged_put->synced_data.complete_multipart_upload_sent = true; @@ -675,7 +662,8 @@ static bool s_s3_auto_ranged_put_update( request = aws_s3_request_new( meta_request, AWS_S3_AUTO_RANGED_PUT_REQUEST_TAG_ABORT_MULTIPART_UPLOAD, - 0, + AWS_S3_REQUEST_TYPE_ABORT_MULTIPART_UPLOAD, + 0 /*part_number*/, AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS | AWS_S3_REQUEST_FLAG_ALWAYS_SEND); auto_ranged_put->synced_data.abort_multipart_upload_sent = true; diff --git a/source/s3_client.c b/source/s3_client.c index 0d071c212..03fc32381 100644 --- a/source/s3_client.c +++ b/source/s3_client.c @@ -1043,7 +1043,13 @@ static struct aws_s3_meta_request *s_s3_client_meta_request_factory_default( * TODO: Still need tests to verify that the request of a part is * splittable or not */ if (aws_http_headers_has(initial_message_headers, aws_byte_cursor_from_c_str("partNumber"))) { - return aws_s3_meta_request_default_new(client->allocator, client, content_length, false, options); + return aws_s3_meta_request_default_new( + client->allocator, + client, + AWS_S3_REQUEST_TYPE_GET_OBJECT, + content_length, + false /*should_compute_content_md5*/, + options); } return aws_s3_meta_request_auto_ranged_get_new(client->allocator, client, client->part_size, options); @@ -1092,6 +1098,7 @@ static struct aws_s3_meta_request *s_s3_client_meta_request_factory_default( return aws_s3_meta_request_default_new( client->allocator, client, + AWS_S3_REQUEST_TYPE_PUT_OBJECT, content_length, client->compute_content_md5 == AWS_MR_CONTENT_MD5_ENABLED && !aws_http_headers_has(initial_message_headers, g_content_md5_header_name), @@ -1138,7 +1145,13 @@ static struct aws_s3_meta_request *s_s3_client_meta_request_factory_default( return aws_s3_meta_request_copy_object_new(client->allocator, client, options); } case AWS_S3_META_REQUEST_TYPE_DEFAULT: - return aws_s3_meta_request_default_new(client->allocator, client, content_length, false, options); + return aws_s3_meta_request_default_new( + client->allocator, + client, + AWS_S3_REQUEST_TYPE_UNKNOWN, + content_length, + false /*should_compute_content_md5*/, + options); default: AWS_FATAL_ASSERT(false); } diff --git a/source/s3_copy_object.c b/source/s3_copy_object.c index a619f0959..2bb76a985 100644 --- a/source/s3_copy_object.c +++ b/source/s3_copy_object.c @@ -37,27 +37,6 @@ static void s_s3_copy_object_request_finished( struct aws_s3_request *request, int error_code); -static int s_s3_copy_object_request_type(const struct aws_s3_request *request) { - switch (request->request_tag) { - case AWS_S3_COPY_OBJECT_REQUEST_TAG_GET_OBJECT_SIZE: - /* It's a HEAD request of GetObject call */ - return AWS_S3_REQUEST_TYPE_HEAD_OBJECT; - case AWS_S3_COPY_OBJECT_REQUEST_TAG_BYPASS: - /* A single copy object request, same as default */ - return AWS_S3_REQUEST_TYPE_DEFAULT; - case AWS_S3_COPY_OBJECT_REQUEST_TAG_CREATE_MULTIPART_UPLOAD: - return AWS_S3_REQUEST_TYPE_CREATE_MULTIPART_UPLOAD; - case AWS_S3_COPY_OBJECT_REQUEST_TAG_MULTIPART_COPY: - return AWS_S3_REQUEST_TYPE_UPLOAD_PART_COPY; - case AWS_S3_COPY_OBJECT_REQUEST_TAG_ABORT_MULTIPART_UPLOAD: - return AWS_S3_REQUEST_TYPE_ABORT_MULTIPART_UPLOAD; - case AWS_S3_COPY_OBJECT_REQUEST_TAG_COMPLETE_MULTIPART_UPLOAD: - return AWS_S3_REQUEST_TYPE_COMPLETE_MULTIPART_UPLOAD; - } - AWS_ASSERT(false); - return AWS_S3_REQUEST_TYPE_MAX; -} - static struct aws_s3_meta_request_vtable s_s3_copy_object_vtable = { .update = s_s3_copy_object_update, .send_request_finish = aws_s3_meta_request_send_request_finish_default, @@ -67,7 +46,6 @@ static struct aws_s3_meta_request_vtable s_s3_copy_object_vtable = { .finished_request = s_s3_copy_object_request_finished, .destroy = s_s3_meta_request_copy_object_destroy, .finish = aws_s3_meta_request_finish_default, - .get_request_type = s_s3_copy_object_request_type, }; /* Allocate a new copy object meta request */ @@ -158,7 +136,8 @@ static bool s_s3_copy_object_update( request = aws_s3_request_new( meta_request, AWS_S3_COPY_OBJECT_REQUEST_TAG_GET_OBJECT_SIZE, - 0, + AWS_S3_REQUEST_TYPE_HEAD_OBJECT, + 0 /*part_number*/, AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS); copy_object->synced_data.head_object_sent = true; @@ -177,7 +156,8 @@ static bool s_s3_copy_object_update( request = aws_s3_request_new( meta_request, AWS_S3_COPY_OBJECT_REQUEST_TAG_BYPASS, - 1, + AWS_S3_REQUEST_TYPE_COPY_OBJECT, + 1 /*part_number*/, AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS); AWS_LOGF_DEBUG( @@ -204,7 +184,8 @@ static bool s_s3_copy_object_update( request = aws_s3_request_new( meta_request, AWS_S3_COPY_OBJECT_REQUEST_TAG_CREATE_MULTIPART_UPLOAD, - 0, + AWS_S3_REQUEST_TYPE_CREATE_MULTIPART_UPLOAD, + 0 /*part_number*/, AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS); copy_object->synced_data.create_multipart_upload_sent = true; @@ -234,11 +215,10 @@ static bool s_s3_copy_object_update( request = aws_s3_request_new( meta_request, AWS_S3_COPY_OBJECT_REQUEST_TAG_MULTIPART_COPY, - 0, + AWS_S3_REQUEST_TYPE_UPLOAD_PART_COPY, + copy_object->threaded_update_data.next_part_number, 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; @@ -263,7 +243,8 @@ static bool s_s3_copy_object_update( request = aws_s3_request_new( meta_request, AWS_S3_COPY_OBJECT_REQUEST_TAG_COMPLETE_MULTIPART_UPLOAD, - 0, + AWS_S3_REQUEST_TYPE_COMPLETE_MULTIPART_UPLOAD, + 0 /*part_number*/, AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS); copy_object->synced_data.complete_multipart_upload_sent = true; @@ -316,7 +297,8 @@ static bool s_s3_copy_object_update( request = aws_s3_request_new( meta_request, AWS_S3_COPY_OBJECT_REQUEST_TAG_ABORT_MULTIPART_UPLOAD, - 0, + AWS_S3_REQUEST_TYPE_ABORT_MULTIPART_UPLOAD, + 0 /*part_number*/, AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS | AWS_S3_REQUEST_FLAG_ALWAYS_SEND); copy_object->synced_data.abort_multipart_upload_sent = true; diff --git a/source/s3_default_meta_request.c b/source/s3_default_meta_request.c index 420eecf80..746122311 100644 --- a/source/s3_default_meta_request.c +++ b/source/s3_default_meta_request.c @@ -51,9 +51,11 @@ static struct aws_s3_meta_request_vtable s_s3_meta_request_default_vtable = { struct aws_s3_meta_request *aws_s3_meta_request_default_new( struct aws_allocator *allocator, struct aws_s3_client *client, + enum aws_s3_request_type request_type, uint64_t content_length, bool should_compute_content_md5, const struct aws_s3_meta_request_options *options) { + AWS_PRECONDITION(allocator); AWS_PRECONDITION(client); AWS_PRECONDITION(options); @@ -103,8 +105,25 @@ struct aws_s3_meta_request *aws_s3_meta_request_default_new( } meta_request_default->content_length = (size_t)content_length; + meta_request_default->request_type = request_type; + + /* Try to get operation name. + * When internal aws-c-s3 code creates a default meta-request, + * a valid request_type is always passed in, and we can get its operation name. + * When external users create a default meta-request, they may have provided + * operation name in the options. */ + const char *operation_name = aws_s3_request_type_operation_name(request_type); + if (operation_name[0] != '\0') { + meta_request_default->operation_name = aws_string_new_from_c_str(allocator, operation_name); + } else if (options->operation_name.len != 0) { + meta_request_default->operation_name = aws_string_new_from_cursor(allocator, &options->operation_name); + } - AWS_LOGF_DEBUG(AWS_LS_S3_META_REQUEST, "id=%p Created new Default Meta Request.", (void *)meta_request_default); + AWS_LOGF_DEBUG( + AWS_LS_S3_META_REQUEST, + "id=%p Created new Default Meta Request. operation=%s", + (void *)meta_request_default, + meta_request_default->operation_name ? aws_string_c_str(meta_request_default->operation_name) : "?"); return &meta_request_default->base; } @@ -114,6 +133,7 @@ static void s_s3_meta_request_default_destroy(struct aws_s3_meta_request *meta_r AWS_PRECONDITION(meta_request->impl); struct aws_s3_meta_request_default *meta_request_default = meta_request->impl; + aws_string_destroy(meta_request_default->operation_name); aws_mem_release(meta_request->allocator, meta_request_default); } @@ -143,7 +163,19 @@ static bool s_s3_meta_request_default_update( goto has_work_remaining; } - request = aws_s3_request_new(meta_request, 0, 1, AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS); + request = aws_s3_request_new( + meta_request, + 0 /*request_tag*/, + meta_request_default->request_type, + 1 /*part_number*/, + AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS); + + /* Default meta-request might know operation name, despite not knowing valid request_type. + * If so, pass the name along. */ + if (request->operation_name == NULL && meta_request_default->operation_name != NULL) { + request->operation_name = + aws_string_new_from_string(meta_request->allocator, meta_request_default->operation_name); + } AWS_LOGF_DEBUG( AWS_LS_S3_META_REQUEST, diff --git a/source/s3_meta_request.c b/source/s3_meta_request.c index 12639aa31..6a6f04f16 100644 --- a/source/s3_meta_request.c +++ b/source/s3_meta_request.c @@ -847,12 +847,6 @@ static void s_s3_meta_request_request_on_signed( s_s3_prepare_request_payload_callback_and_destroy(payload, error_code); } -static bool s_s3_request_is_upload_part(struct aws_s3_request *request) { - struct aws_s3_meta_request *meta_request = request->meta_request; - return meta_request->type == AWS_S3_META_REQUEST_TYPE_PUT_OBJECT && meta_request->vtable->get_request_type && - meta_request->vtable->get_request_type(request) == AWS_S3_REQUEST_TYPE_UPLOAD_PART; -} - void aws_s3_meta_request_send_request(struct aws_s3_meta_request *meta_request, struct aws_s3_connection *connection) { AWS_PRECONDITION(meta_request); AWS_PRECONDITION(connection); @@ -875,7 +869,7 @@ void aws_s3_meta_request_send_request(struct aws_s3_meta_request *meta_request, options.on_metrics = s_s3_meta_request_stream_metrics; } options.on_complete = s_s3_meta_request_stream_complete; - if (s_s3_request_is_upload_part(request)) { + if (request->request_type == AWS_S3_REQUEST_TYPE_UPLOAD_PART) { options.response_first_byte_timeout_ms = aws_atomic_load_int(&meta_request->client->upload_timeout_ms); request->upload_timeout_ms = (size_t)options.response_first_byte_timeout_ms; } @@ -1061,7 +1055,7 @@ static int s_s3_meta_request_incoming_headers( s_s3_meta_request_error_code_from_response_status(request->send_data.response_status) == AWS_ERROR_SUCCESS; if (successful_response && meta_request->checksum_config.validate_response_checksum && - request->request_tag == AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_PART) { + request->request_type == AWS_S3_REQUEST_TYPE_GET_OBJECT) { s_get_part_response_headers_checksum_helper(connection, meta_request, headers, headers_count); } @@ -1811,6 +1805,11 @@ void aws_s3_meta_request_result_setup( aws_byte_buf_init_copy( result->error_response_body, meta_request->allocator, &failed_request->send_data.response_body); } + + if (failed_request->operation_name != NULL) { + result->error_response_operation_name = + aws_string_new_from_string(meta_request->allocator, failed_request->operation_name); + } } result->response_status = response_status; @@ -1830,6 +1829,8 @@ void aws_s3_meta_request_result_clean_up( aws_mem_release(meta_request->allocator, result->error_response_body); } + aws_string_destroy(result->error_response_operation_name); + AWS_ZERO_STRUCT(*result); } diff --git a/source/s3_request.c b/source/s3_request.c index d1f91f218..f9c92e285 100644 --- a/source/s3_request.c +++ b/source/s3_request.c @@ -16,8 +16,10 @@ static void s_s3_request_destroy(void *user_data); struct aws_s3_request *aws_s3_request_new( struct aws_s3_meta_request *meta_request, int request_tag, + enum aws_s3_request_type request_type, uint32_t part_number, uint32_t flags) { + AWS_PRECONDITION(meta_request); AWS_PRECONDITION(meta_request->allocator); @@ -29,6 +31,13 @@ struct aws_s3_request *aws_s3_request_new( request->meta_request = aws_s3_meta_request_acquire(meta_request); request->request_tag = request_tag; + request->request_type = request_type; + + const char *operation_name = aws_s3_request_type_operation_name(request_type); + if (operation_name[0] != '\0') { + request->operation_name = aws_string_new_from_c_str(request->allocator, operation_name); + } + request->part_number = part_number; request->record_response_headers = (flags & AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS) != 0; request->has_part_size_response_body = (flags & AWS_S3_REQUEST_FLAG_PART_SIZE_RESPONSE_BODY) != 0; @@ -45,14 +54,7 @@ void aws_s3_request_setup_send_data(struct aws_s3_request *request, struct aws_h aws_s3_request_clean_up_send_data(request); request->send_data.message = message; - struct aws_s3_meta_request *meta_request = request->meta_request; - request->send_data.metrics = aws_s3_request_metrics_new(request->allocator, message); - if (!meta_request->vtable->get_request_type) { - request->send_data.metrics->req_resp_info_metrics.request_type = AWS_S3_REQUEST_TYPE_DEFAULT; - } else { - request->send_data.metrics->req_resp_info_metrics.request_type = - meta_request->vtable->get_request_type(request); - } + request->send_data.metrics = aws_s3_request_metrics_new(request->allocator, request, message); /* Start the timestamp */ aws_high_res_clock_get_ticks((uint64_t *)&request->send_data.metrics->time_metrics.start_timestamp_ns); @@ -124,6 +126,7 @@ static void s_s3_request_destroy(void *user_data) { aws_s3_request_clean_up_send_data(request); aws_byte_buf_clean_up(&request->request_body); aws_s3_buffer_pool_release_ticket(request->meta_request->client->buffer_pool, request->ticket); + aws_string_destroy(request->operation_name); aws_s3_meta_request_release(request->meta_request); aws_mem_release(request->allocator, request); @@ -138,6 +141,7 @@ static void s_s3_request_metrics_destroy(void *arg) { aws_string_destroy(metrics->req_resp_info_metrics.request_path_query); aws_string_destroy(metrics->req_resp_info_metrics.host_address); aws_string_destroy(metrics->req_resp_info_metrics.request_id); + aws_string_destroy(metrics->req_resp_info_metrics.operation_name); aws_string_destroy(metrics->crt_info_metrics.ip_address); aws_mem_release(metrics->allocator, metrics); @@ -145,7 +149,9 @@ static void s_s3_request_metrics_destroy(void *arg) { struct aws_s3_request_metrics *aws_s3_request_metrics_new( struct aws_allocator *allocator, - struct aws_http_message *message) { + const struct aws_s3_request *request, + const struct aws_http_message *message) { + struct aws_s3_request_metrics *metrics = aws_mem_calloc(allocator, 1, sizeof(struct aws_s3_request_metrics)); metrics->allocator = allocator; struct aws_byte_cursor out_path; @@ -166,6 +172,12 @@ struct aws_s3_request_metrics *aws_s3_request_metrics_new( metrics->req_resp_info_metrics.host_address = aws_string_new_from_cursor(allocator, &host_header_value); AWS_ASSERT(metrics->req_resp_info_metrics.host_address != NULL); + metrics->req_resp_info_metrics.request_type = request->request_type; + + if (request->operation_name != NULL) { + metrics->req_resp_info_metrics.operation_name = aws_string_new_from_string(allocator, request->operation_name); + } + metrics->time_metrics.start_timestamp_ns = -1; metrics->time_metrics.end_timestamp_ns = -1; metrics->time_metrics.total_duration_ns = -1; @@ -384,6 +396,18 @@ int aws_s3_request_metrics_get_request_stream_id(const struct aws_s3_request_met return AWS_OP_SUCCESS; } +int aws_s3_request_metrics_get_operation_name( + const struct aws_s3_request_metrics *metrics, + const struct aws_string **out_operation_name) { + AWS_PRECONDITION(metrics); + AWS_PRECONDITION(out_operation_name); + if (metrics->req_resp_info_metrics.operation_name == NULL) { + return aws_raise_error(AWS_ERROR_S3_METRIC_DATA_NOT_AVAILABLE); + } + *out_operation_name = metrics->req_resp_info_metrics.operation_name; + return AWS_OP_SUCCESS; +} + void aws_s3_request_metrics_get_request_type( const struct aws_s3_request_metrics *metrics, enum aws_s3_request_type *out_request_type) { diff --git a/source/s3_util.c b/source/s3_util.c index 451179fb0..c1137c41b 100644 --- a/source/s3_util.c +++ b/source/s3_util.c @@ -64,6 +64,33 @@ const struct aws_byte_cursor g_user_agent_header_product_name = const uint32_t g_s3_max_num_upload_parts = 10000; const size_t g_s3_min_upload_part_size = MB_TO_BYTES(5); +const char *aws_s3_request_type_operation_name(enum aws_s3_request_type type) { + switch (type) { + case AWS_S3_REQUEST_TYPE_HEAD_OBJECT: + return "HeadObject"; + case AWS_S3_REQUEST_TYPE_GET_OBJECT: + return "GetObject"; + case AWS_S3_REQUEST_TYPE_LIST_PARTS: + return "ListParts"; + case AWS_S3_REQUEST_TYPE_CREATE_MULTIPART_UPLOAD: + return "CreateMultipartUpload"; + case AWS_S3_REQUEST_TYPE_UPLOAD_PART: + return "UploadPart"; + case AWS_S3_REQUEST_TYPE_ABORT_MULTIPART_UPLOAD: + return "AbortMultipartUpload"; + case AWS_S3_REQUEST_TYPE_COMPLETE_MULTIPART_UPLOAD: + return "CompleteMultipartUpload"; + case AWS_S3_REQUEST_TYPE_UPLOAD_PART_COPY: + return "UploadPartCopy"; + case AWS_S3_REQUEST_TYPE_COPY_OBJECT: + return "CopyObject"; + case AWS_S3_REQUEST_TYPE_PUT_OBJECT: + return "PutObject"; + default: + return ""; + } +} + void copy_http_headers(const struct aws_http_headers *src, struct aws_http_headers *dest) { AWS_PRECONDITION(src); AWS_PRECONDITION(dest); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ec59e1285..bc9d2c612 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -159,6 +159,7 @@ add_net_test_case(test_s3_get_object_invoke_headers_callback_on_error) add_net_test_case(test_s3_put_object_invoke_headers_callback_on_error) add_net_test_case(test_s3_put_object_invoke_headers_callback_on_error_with_user_cancellation) add_net_test_case(test_s3_default_fail_body_callback) +add_net_test_case(test_s3_default_fail_operation_name) add_net_test_case(test_s3_error_missing_file) add_net_test_case(test_s3_existing_host_entry) add_net_test_case(test_s3_put_fail_object_invalid_request) @@ -178,6 +179,7 @@ add_net_test_case(test_s3_not_satisfiable_range) add_net_test_case(test_s3_bad_endpoint) add_net_test_case(test_s3_different_endpoints) +add_test_case(test_s3_request_type_operation_name) add_test_case(test_s3_replace_quote_entities) add_test_case(test_s3_strip_quotes) add_test_case(test_s3_parse_content_range_response_header) diff --git a/tests/s3_data_plane_tests.c b/tests/s3_data_plane_tests.c index bcd03f293..c031f2c1c 100644 --- a/tests/s3_data_plane_tests.c +++ b/tests/s3_data_plane_tests.c @@ -368,6 +368,7 @@ static int s_test_s3_request_create_destroy(struct aws_allocator *allocator, voi (void)ctx; const int request_tag = 1234; + const enum aws_s3_request_type request_type = AWS_S3_REQUEST_TYPE_LIST_PARTS; const uint32_t part_number = 5678; struct aws_s3_tester tester; @@ -384,14 +385,16 @@ static int s_test_s3_request_create_destroy(struct aws_allocator *allocator, voi struct aws_http_message *request_message = aws_s3_tester_dummy_http_request_new(&tester); ASSERT_TRUE(request_message != NULL); - struct aws_s3_request *request = - aws_s3_request_new(meta_request, request_tag, part_number, AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS); + struct aws_s3_request *request = aws_s3_request_new( + meta_request, request_tag, request_type, part_number, AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS); ASSERT_TRUE(request != NULL); ASSERT_TRUE(request->meta_request == meta_request); ASSERT_TRUE(request->part_number == part_number); ASSERT_TRUE(request->request_tag == request_tag); + ASSERT_TRUE(request->request_type == request_type); + ASSERT_STR_EQUALS("ListParts", aws_string_c_str(request->operation_name)); ASSERT_TRUE(request->record_response_headers == true); aws_s3_request_setup_send_data(request, request_message); @@ -493,7 +496,8 @@ static int s_test_s3_meta_request_body_streaming(struct aws_allocator *allocator /* Queue the first range of parts in order. Each part should be flushed one-by-one. */ { for (uint32_t part_number = part_range0_start; part_number <= part_range0_end; ++part_number) { - struct aws_s3_request *request = aws_s3_request_new(meta_request, 0, part_number, 0); + struct aws_s3_request *request = aws_s3_request_new( + meta_request, 0 /*request_tag*/, AWS_S3_REQUEST_TYPE_GET_OBJECT, part_number, 0 /*flags*/); aws_s3_get_part_range( 0ULL, @@ -529,7 +533,8 @@ static int s_test_s3_meta_request_body_streaming(struct aws_allocator *allocator ASSERT_TRUE(part_range1_start != part_range1_end); for (uint32_t part_number = part_range1_start + 1; part_number <= part_range1_end; ++part_number) { - struct aws_s3_request *request = aws_s3_request_new(meta_request, 0, part_number, 0); + struct aws_s3_request *request = aws_s3_request_new( + meta_request, 0 /*request_tag*/, AWS_S3_REQUEST_TYPE_GET_OBJECT, part_number, 0 /*flags*/); aws_s3_get_part_range( 0ULL, @@ -557,7 +562,8 @@ static int s_test_s3_meta_request_body_streaming(struct aws_allocator *allocator /* Stream the last part of the body, which should flush the priority queue. */ { - struct aws_s3_request *request = aws_s3_request_new(meta_request, 0, part_range1_start, 0); + struct aws_s3_request *request = aws_s3_request_new( + meta_request, 0 /*request_tag*/, AWS_S3_REQUEST_TYPE_GET_OBJECT, part_range1_start, 0 /*flags*/); aws_s3_get_part_range( 0ULL, @@ -607,15 +613,15 @@ static int s_test_s3_client_queue_requests(struct aws_allocator *allocator, void struct aws_s3_meta_request *mock_meta_request = aws_s3_tester_mock_meta_request_new(&tester); mock_meta_request->client = aws_s3_client_acquire(mock_client); - struct aws_s3_request *pivot_request = aws_s3_request_new(mock_meta_request, 0, 0, 0); + struct aws_s3_request *pivot_request = aws_s3_request_new(mock_meta_request, 0, 0, 0, 0); struct aws_linked_list pivot_request_list; aws_linked_list_init(&pivot_request_list); struct aws_s3_request *requests[] = { - aws_s3_request_new(mock_meta_request, 0, 0, 0), - aws_s3_request_new(mock_meta_request, 0, 0, 0), - aws_s3_request_new(mock_meta_request, 0, 0, 0), + aws_s3_request_new(mock_meta_request, 0, 0, 0, 0), + aws_s3_request_new(mock_meta_request, 0, 0, 0, 0), + aws_s3_request_new(mock_meta_request, 0, 0, 0, 0), }; const uint32_t num_requests = AWS_ARRAY_SIZE(requests); @@ -728,7 +734,7 @@ static bool s_s3_test_work_meta_request_update( if (out_request) { if (user_data->has_work_remaining) { - *out_request = aws_s3_request_new(meta_request, 0, 0, 0); + *out_request = aws_s3_request_new(meta_request, 0, 0, 0, 0); } } @@ -989,7 +995,7 @@ static int s_test_s3_client_update_connections_finish_result(struct aws_allocato /* Verify that the request does not get sent because the meta request has finish-result. */ { - struct aws_s3_request *request = aws_s3_request_new(mock_meta_request, 0, 0, 0); + struct aws_s3_request *request = aws_s3_request_new(mock_meta_request, 0, 0, 0, 0); aws_linked_list_push_back(&mock_client->threaded_data.request_queue, &request->node); ++mock_client->threaded_data.request_queue_size; @@ -1010,7 +1016,8 @@ static int s_test_s3_client_update_connections_finish_result(struct aws_allocato /* Verify that a request with the 'always send' flag still gets sent when the meta request has a finish-result. */ { - struct aws_s3_request *request = aws_s3_request_new(mock_meta_request, 0, 0, AWS_S3_REQUEST_FLAG_ALWAYS_SEND); + struct aws_s3_request *request = + aws_s3_request_new(mock_meta_request, 0, 0, 0, AWS_S3_REQUEST_FLAG_ALWAYS_SEND); aws_linked_list_push_back(&mock_client->threaded_data.request_queue, &request->node); ++mock_client->threaded_data.request_queue_size; @@ -4043,6 +4050,11 @@ static int s_test_s3_error_missing_file(struct aws_allocator *allocator, void *c ASSERT_TRUE(meta_request_test_results.error_response_headers != NULL); + ASSERT_NOT_NULL(meta_request_test_results.error_response_operation_name); + ASSERT_TRUE( + aws_string_eq_c_str(meta_request_test_results.error_response_operation_name, "GetObject") || + aws_string_eq_c_str(meta_request_test_results.error_response_operation_name, "HeadObject")); + meta_request = aws_s3_meta_request_release(meta_request); aws_s3_tester_wait_for_meta_request_shutdown(&tester); @@ -4577,6 +4589,42 @@ static int s_test_s3_default_fail_body_callback(struct aws_allocator *allocator, return 0; } +/* Test that if a DEFAULt meta-request sets the operation_name, and gets an error response, + * then aws_s3_meta_request_result.error_response_operation_name is set. */ +AWS_TEST_CASE(test_s3_default_fail_operation_name, s_test_s3_default_fail_operation_name) +static int s_test_s3_default_fail_operation_name(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + struct aws_s3_meta_request_test_results meta_request_test_results; + aws_s3_meta_request_test_results_init(&meta_request_test_results, allocator); + + struct aws_byte_cursor invalid_path = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("___INVALID_PATH___"); + + struct aws_s3_tester_meta_request_options options = { + .allocator = allocator, + .meta_request_type = AWS_S3_META_REQUEST_TYPE_DEFAULT, + + .validate_type = AWS_S3_TESTER_VALIDATE_TYPE_EXPECT_FAILURE, + .default_type_options = + { + .mode = AWS_S3_TESTER_DEFAULT_TYPE_MODE_GET, + .operation_name = aws_byte_cursor_from_c_str("GetObject"), + }, + .get_options = + { + .object_path = invalid_path, + }, + }; + + ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(NULL, &options, &meta_request_test_results)); + ASSERT_TRUE(meta_request_test_results.finished_error_code == AWS_ERROR_S3_INVALID_RESPONSE_STATUS); + ASSERT_STR_EQUALS("GetObject", aws_string_c_str(meta_request_test_results.error_response_operation_name)); + + aws_s3_meta_request_test_results_clean_up(&meta_request_test_results); + + return 0; +} + AWS_TEST_CASE(test_s3_put_fail_object_invalid_request, s_test_s3_put_fail_object_invalid_request) static int s_test_s3_put_fail_object_invalid_request(struct aws_allocator *allocator, void *ctx) { (void)ctx; @@ -4598,6 +4646,9 @@ static int s_test_s3_put_fail_object_invalid_request(struct aws_allocator *alloc ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(NULL, &options, &meta_request_test_results)); ASSERT_UINT_EQUALS(AWS_ERROR_S3_INVALID_RESPONSE_STATUS, meta_request_test_results.finished_error_code); + /* Since 1MB is under part_size, there will be a single PutObject request */ + ASSERT_STR_EQUALS("PutObject", aws_string_c_str(meta_request_test_results.error_response_operation_name)); + aws_s3_meta_request_test_results_clean_up(&meta_request_test_results); return 0; @@ -5416,6 +5467,10 @@ static int s_test_s3_not_satisfiable_range(struct aws_allocator *allocator, void ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(&tester, &options, &results)); ASSERT_TRUE(results.finished_response_status == AWS_HTTP_STATUS_CODE_416_REQUESTED_RANGE_NOT_SATISFIABLE); + ASSERT_NOT_NULL(results.error_response_operation_name); + ASSERT_TRUE( + aws_string_eq_c_str(results.error_response_operation_name, "GetObject") || + aws_string_eq_c_str(results.error_response_operation_name, "HeadObject")); aws_s3_meta_request_test_results_clean_up(&results); @@ -6859,6 +6914,10 @@ static int s_test_s3_upload_review_rejection(struct aws_allocator *allocator, vo }; ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(&tester, &get_options, &test_results)); ASSERT_INT_EQUALS(AWS_HTTP_STATUS_CODE_404_NOT_FOUND, test_results.finished_response_status); + ASSERT_NOT_NULL(test_results.error_response_operation_name); + ASSERT_TRUE( + aws_string_eq_c_str(test_results.error_response_operation_name, "GetObject") || + aws_string_eq_c_str(test_results.error_response_operation_name, "HeadObject")); aws_s3_meta_request_test_results_clean_up(&test_results); aws_s3_client_release(client); diff --git a/tests/s3_mock_server_tests.c b/tests/s3_mock_server_tests.c index 47a538071..5d24e5a9e 100644 --- a/tests/s3_mock_server_tests.c +++ b/tests/s3_mock_server_tests.c @@ -85,6 +85,10 @@ static int s_validate_mpu_mock_server_metrics(struct aws_array_list *metrics_lis aws_s3_request_metrics_get_request_type(metrics, &request_type); ASSERT_UINT_EQUALS(AWS_S3_REQUEST_TYPE_CREATE_MULTIPART_UPLOAD, request_type); + const struct aws_string *operation_name = NULL; + ASSERT_SUCCESS(aws_s3_request_metrics_get_operation_name(metrics, &operation_name)); + ASSERT_STR_EQUALS("CreateMultipartUpload", aws_string_c_str(operation_name)); + /* Second metrics should be the Upload Part */ aws_array_list_get_at(metrics_list, (void **)&metrics, 1); struct aws_byte_cursor header_value; @@ -98,18 +102,24 @@ static int s_validate_mpu_mock_server_metrics(struct aws_array_list *metrics_lis request_type = 0; aws_s3_request_metrics_get_request_type(metrics, &request_type); ASSERT_UINT_EQUALS(AWS_S3_REQUEST_TYPE_UPLOAD_PART, request_type); + ASSERT_SUCCESS(aws_s3_request_metrics_get_operation_name(metrics, &operation_name)); + ASSERT_STR_EQUALS("UploadPart", aws_string_c_str(operation_name)); /* Third metrics still be Upload Part */ aws_array_list_get_at(metrics_list, (void **)&metrics, 2); request_type = 0; aws_s3_request_metrics_get_request_type(metrics, &request_type); ASSERT_UINT_EQUALS(AWS_S3_REQUEST_TYPE_UPLOAD_PART, request_type); + ASSERT_SUCCESS(aws_s3_request_metrics_get_operation_name(metrics, &operation_name)); + ASSERT_STR_EQUALS("UploadPart", aws_string_c_str(operation_name)); /* Fourth should be complete MPU */ aws_array_list_get_at(metrics_list, (void **)&metrics, 3); request_type = 0; aws_s3_request_metrics_get_request_type(metrics, &request_type); ASSERT_UINT_EQUALS(AWS_S3_REQUEST_TYPE_COMPLETE_MULTIPART_UPLOAD, request_type); + ASSERT_SUCCESS(aws_s3_request_metrics_get_operation_name(metrics, &operation_name)); + ASSERT_STR_EQUALS("CompleteMultipartUpload", aws_string_c_str(operation_name)); /* All the rest should be similar */ return AWS_OP_SUCCESS; @@ -321,6 +331,7 @@ TEST_CASE(async_access_denied_from_complete_multipart_mock_server) { ASSERT_UINT_EQUALS(AWS_ERROR_S3_NON_RECOVERABLE_ASYNC_ERROR, out_results.finished_error_code); ASSERT_UINT_EQUALS(AWS_S3_RESPONSE_STATUS_SUCCESS, out_results.finished_response_status); ASSERT_TRUE(out_results.error_response_body.len != 0); + ASSERT_STR_EQUALS("CompleteMultipartUpload", aws_string_c_str(out_results.error_response_operation_name)); aws_s3_meta_request_test_results_clean_up(&out_results); aws_s3_client_release(client); @@ -363,6 +374,7 @@ TEST_CASE(get_object_modified_mock_server) { ASSERT_UINT_EQUALS(AWS_ERROR_S3_OBJECT_MODIFIED, out_results.finished_error_code); ASSERT_UINT_EQUALS(AWS_HTTP_STATUS_CODE_412_PRECONDITION_FAILED, out_results.finished_response_status); + ASSERT_STR_EQUALS("GetObject", aws_string_c_str(out_results.error_response_operation_name)); aws_s3_meta_request_test_results_clean_up(&out_results); aws_s3_client_release(client); @@ -743,6 +755,7 @@ TEST_CASE(resume_list_parts_failed_mock_server) { ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(&tester, &put_options, &out_results)); ASSERT_UINT_EQUALS(AWS_ERROR_S3_INVALID_RESPONSE_STATUS, out_results.finished_error_code); ASSERT_UINT_EQUALS(AWS_HTTP_STATUS_CODE_404_NOT_FOUND, out_results.finished_response_status); + ASSERT_STR_EQUALS("ListParts", aws_string_c_str(out_results.error_response_operation_name)); aws_s3_meta_request_test_results_clean_up(&out_results); aws_s3_meta_request_resume_token_release(token); diff --git a/tests/s3_tester.c b/tests/s3_tester.c index a3425cf5f..cb8b9afa6 100644 --- a/tests/s3_tester.c +++ b/tests/s3_tester.c @@ -171,6 +171,11 @@ static void s_s3_test_meta_request_finish( &meta_request_test_results->error_response_body, tester->allocator, result->error_response_body); } + if (result->error_response_operation_name != NULL) { + meta_request_test_results->error_response_operation_name = + aws_string_new_from_string(tester->allocator, result->error_response_operation_name); + } + meta_request_test_results->finished_response_status = result->response_status; meta_request_test_results->finished_error_code = result->error_code; @@ -496,6 +501,7 @@ void aws_s3_meta_request_test_results_clean_up(struct aws_s3_meta_request_test_r aws_http_headers_release(test_meta_request->error_response_headers); aws_byte_buf_clean_up(&test_meta_request->error_response_body); + aws_string_destroy(test_meta_request->error_response_operation_name); aws_http_headers_release(test_meta_request->response_headers); while (aws_array_list_length(&test_meta_request->synced_data.metrics) > 0) { struct aws_s3_request_metrics *metrics = NULL; @@ -1396,6 +1402,7 @@ int aws_s3_tester_send_meta_request_with_options( struct aws_s3_meta_request_options meta_request_options = { .type = options->meta_request_type, + .operation_name = options->default_type_options.operation_name, .message = options->message, .checksum_config = &checksum_config, .resume_token = options->put_options.resume_token, @@ -1830,6 +1837,7 @@ int aws_s3_tester_validate_get_object_results( ASSERT_TRUE(meta_request_test_results->error_response_headers == NULL); ASSERT_TRUE(meta_request_test_results->error_response_body.len == 0); + ASSERT_NULL(meta_request_test_results->error_response_operation_name); struct aws_byte_cursor sse_byte_cursor; @@ -1967,6 +1975,7 @@ int aws_s3_tester_validate_put_object_results( ASSERT_TRUE(meta_request_test_results->error_response_headers == NULL); ASSERT_TRUE(meta_request_test_results->error_response_body.len == 0); + ASSERT_NULL(meta_request_test_results->error_response_operation_name); struct aws_byte_cursor etag_byte_cursor; AWS_ZERO_STRUCT(etag_byte_cursor); diff --git a/tests/s3_tester.h b/tests/s3_tester.h index c35462f62..624e797b4 100644 --- a/tests/s3_tester.h +++ b/tests/s3_tester.h @@ -169,6 +169,7 @@ struct aws_s3_tester_meta_request_options { /* Default Meta Request specific options. */ struct { enum aws_s3_tester_default_type_mode mode; + struct aws_byte_cursor operation_name; } default_type_options; /* Get Object Meta Request specific options.*/ @@ -222,6 +223,7 @@ struct aws_s3_meta_request_test_results { struct aws_http_headers *error_response_headers; struct aws_byte_buf error_response_body; + struct aws_string *error_response_operation_name; size_t part_size; int headers_response_status; diff --git a/tests/s3_util_tests.c b/tests/s3_util_tests.c index 1233c391a..433dd32f2 100644 --- a/tests/s3_util_tests.c +++ b/tests/s3_util_tests.c @@ -22,6 +22,30 @@ #include #include +AWS_TEST_CASE(test_s3_request_type_operation_name, s_test_s3_request_type_operation_name) +static int s_test_s3_request_type_operation_name(struct aws_allocator *allocator, void *ctx) { + (void)allocator; + (void)ctx; + + /* sanity check */ + ASSERT_STR_EQUALS("HeadObject", aws_s3_request_type_operation_name(AWS_S3_REQUEST_TYPE_HEAD_OBJECT)); + + /* check that all valid enums give back valid strings */ + for (enum aws_s3_request_type type = AWS_S3_REQUEST_TYPE_UNKNOWN + 1; type < AWS_S3_REQUEST_TYPE_MAX; ++type) { + const char *operation_name = aws_s3_request_type_operation_name(type); + ASSERT_NOT_NULL(operation_name); + ASSERT_TRUE(strlen(operation_name) > 1); + } + + /* check that invalid enums give back empty strings */ + ASSERT_NOT_NULL(aws_s3_request_type_operation_name(AWS_S3_REQUEST_TYPE_UNKNOWN)); + ASSERT_STR_EQUALS("", aws_s3_request_type_operation_name(AWS_S3_REQUEST_TYPE_UNKNOWN)); + ASSERT_STR_EQUALS("", aws_s3_request_type_operation_name(AWS_S3_REQUEST_TYPE_MAX)); + ASSERT_STR_EQUALS("", aws_s3_request_type_operation_name(-1)); + + return 0; +} + AWS_TEST_CASE(test_s3_replace_quote_entities, s_test_s3_replace_quote_entities) static int s_test_s3_replace_quote_entities(struct aws_allocator *allocator, void *ctx) { (void)ctx;