Skip to content

Commit 7d6b98a

Browse files
committed
Add test and update mock server
1 parent b3f48c7 commit 7d6b98a

File tree

8 files changed

+156
-12
lines changed

8 files changed

+156
-12
lines changed

source/s3express_credentials_provider.c

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -397,16 +397,20 @@ static void s_on_request_finished(
397397
static struct aws_http_message *s_create_session_request_new(
398398
struct aws_allocator *allocator,
399399
struct aws_byte_cursor host_value,
400-
struct aws_http_headers *headers) {
400+
struct aws_http_headers *headers,
401+
const struct aws_uri *endpoint_override) {
401402
struct aws_http_message *request = aws_http_message_new_request(allocator);
402403

403404
struct aws_http_header host_header = {
404405
.name = g_host_header_name,
405406
.value = host_value,
406407
};
407408

408-
if (aws_http_message_add_header(request, host_header)) {
409-
goto error;
409+
/* NOTE: ONLY FOR TESTS. Don't add the host header for endpoint override. */
410+
if (endpoint_override == NULL) {
411+
if (aws_http_message_add_header(request, host_header)) {
412+
goto error;
413+
}
410414
}
411415

412416
struct aws_http_header user_agent_header = {
@@ -436,7 +440,14 @@ static struct aws_http_message *s_create_session_request_new(
436440
goto error;
437441
}
438442

439-
if (aws_http_message_set_request_path(request, s_create_session_path_query)) {
443+
struct aws_byte_cursor path_and_query = s_create_session_path_query;
444+
if (endpoint_override != NULL) {
445+
const struct aws_byte_cursor *override_path_query = aws_uri_path_and_query(endpoint_override);
446+
if (override_path_query->len > 0) {
447+
path_and_query = *override_path_query;
448+
}
449+
}
450+
if (aws_http_message_set_request_path(request, path_and_query)) {
440451
goto error;
441452
}
442453
return request;
@@ -528,15 +539,14 @@ static struct aws_s3express_session_creator *s_session_creator_new(
528539
const struct aws_credentials_properties_s3express *s3express_properties) {
529540

530541
struct aws_s3express_credentials_provider_impl *impl = provider->impl;
531-
struct aws_http_message *request =
532-
s_create_session_request_new(provider->allocator, s3express_properties->host, s3express_properties->headers);
542+
struct aws_http_message *request = s_create_session_request_new(
543+
provider->allocator,
544+
s3express_properties->host,
545+
s3express_properties->headers,
546+
impl->mock_test.endpoint_override);
533547
if (!request) {
534548
return NULL;
535549
}
536-
if (impl->mock_test.endpoint_override) {
537-
/* NOTE: ONLY FOR TESTS. Erase the host header for endpoint override. */
538-
aws_http_headers_erase(aws_http_message_get_headers(request), g_host_header_name);
539-
}
540550

541551
struct aws_s3express_session_creator *session_creator =
542552
aws_mem_calloc(provider->allocator, 1, sizeof(struct aws_s3express_session_creator));

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ if(ENABLE_MOCK_SERVER_TESTS)
351351
add_net_test_case(s3express_provider_background_refresh_mock_server)
352352
add_net_test_case(s3express_provider_background_refresh_remove_inactive_creds_mock_server)
353353
add_net_test_case(s3express_provider_stress_mock_server)
354+
add_net_test_case(s3express_provider_get_credentials_kms_headers_mock_server)
354355

355356
add_net_test_case(s3express_client_sanity_test_mock_server)
356357
add_net_test_case(s3express_client_sanity_override_test_mock_server)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"status": 200,
3+
"headers": {"Connection": "close",
4+
"x-amz-server-side-encryption": "aws:kms"},
5+
"body": [
6+
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
7+
"<CompleteMultipartUploadResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">",
8+
"<Location>http://default.s3.us-west-2.amazonaws.com/default</Location>",
9+
"<Bucket>default</Bucket>",
10+
"<Key>default</Key>",
11+
"<ETag>\"3858f62230ac3c915f300c664312c11f-9\"</ETag>",
12+
"</CompleteMultipartUploadResult>"
13+
]
14+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"status": 200,
3+
"headers": {
4+
"x-amz-request-id": "12345",
5+
"x-amz-server-side-encryption": "aws:kms"
6+
},
7+
"request_headers": {
8+
"x-amz-server-side-encryption": "aws:kms"
9+
},
10+
"body": [
11+
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
12+
"<CreateSessionResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">",
13+
"<Credentials>",
14+
"<SessionToken>sessionToken</SessionToken>",
15+
"<SecretAccessKey>secretKey</SecretAccessKey>",
16+
"<AccessKeyId>accessKeyId</AccessKeyId>",
17+
"<Expiration>2023-06-26T17:33:30Z</Expiration>",
18+
"</Credentials>",
19+
"</CreateSessionResult>"
20+
]
21+
}

tests/mock_s3_server/mock_s3_server.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class ResponseConfig:
5656
json_path: str = None
5757
throttle: bool = False
5858
force_retry: bool = False
59-
59+
request_headers = None
6060
def _resolve_file_path(self, wrapper, request_type):
6161
global SHOULD_THROTTLE
6262
if self.json_path is None:
@@ -85,6 +85,16 @@ def resolve_response(self, wrapper, request_type, chunked=False, head_request=Fa
8585
".\n generate_body_size: ", self.generate_body_size)
8686
with open(self.json_path, 'r') as f:
8787
data = json.load(f)
88+
headers = wrapper.basic_headers()
89+
90+
# If request_headers is present, validate that the request contains all required headers
91+
if 'request_headers' in data:
92+
for header in data['request_headers']:
93+
header_bytes = header.encode('utf-8')
94+
if not any(header_bytes == h[0] for h in self.request_headers):
95+
response = Response(status_code=500, delay=0, headers=headers,
96+
data=json.dumps({'error': f"Missing required header: {header}"}), chunked=chunked, head_request=head_request)
97+
return response
8898

8999
# if response has delay, then sleep before sending it
90100
delay = data.get('delay', 0)
@@ -95,7 +105,6 @@ def resolve_response(self, wrapper, request_type, chunked=False, head_request=Fa
95105
else:
96106
body = "\n".join(data['body'])
97107

98-
headers = wrapper.basic_headers()
99108
content_length_set = False
100109
for header in data['headers'].items():
101110
headers.append((header[0], str(header[1])))
@@ -499,6 +508,7 @@ async def handle_mock_s3_request(wrapper, request):
499508

500509
if response_config is None:
501510
response_config = ResponseConfig(parsed_path.path)
511+
response_config.request_headers = request.headers
502512

503513
response = response_config.resolve_response(
504514
wrapper, request_type, head_request=method == "HEAD")

tests/s3_mock_server_s3express_provider_test.c

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ struct aws_s3express_provider_tester {
4545
struct aws_uri mock_server;
4646
struct aws_s3_client *client;
4747

48+
aws_simple_completion_callback *on_provider_shutdown_callback;
49+
void *shutdown_user_data;
4850
int error_code;
4951
};
5052

@@ -252,6 +254,87 @@ TEST_CASE(s3express_provider_get_credentials_mock_server) {
252254
return AWS_OP_SUCCESS;
253255
}
254256

257+
struct aws_s3express_credentials_provider *s_s3express_credentials_provider_factory(
258+
struct aws_allocator *allocator,
259+
struct aws_s3_client *client,
260+
aws_simple_completion_callback on_provider_shutdown_callback,
261+
void *shutdown_user_data,
262+
void *factory_user_data) {
263+
(void)allocator;
264+
(void)client;
265+
(void)on_provider_shutdown_callback;
266+
(void)shutdown_user_data;
267+
(void)factory_user_data;
268+
s_s3express_tester.on_provider_shutdown_callback = on_provider_shutdown_callback;
269+
s_s3express_tester.shutdown_user_data = shutdown_user_data;
270+
struct aws_s3express_credentials_provider_default_options options = {
271+
.client = client,
272+
// .shutdown_complete_callback = s_on_shutdown_complete,
273+
// .shutdown_user_data = &s_s3express_tester,
274+
.mock_test.bg_refresh_secs_override = s_bg_refresh_secs_override,
275+
};
276+
struct aws_s3express_credentials_provider *provider =
277+
aws_s3express_credentials_provider_new_default(allocator, &options);
278+
struct aws_s3express_credentials_provider_impl *impl = provider->impl;
279+
impl->mock_test.endpoint_override = &s_s3express_tester.mock_server;
280+
impl->mock_test.s3express_session_is_valid_override = s_s3express_session_always_true;
281+
282+
return provider;
283+
}
284+
285+
TEST_CASE(s3express_provider_get_credentials_kms_headers_mock_server) {
286+
(void)ctx;
287+
288+
struct aws_s3_tester tester;
289+
ASSERT_SUCCESS(aws_s3_tester_init(allocator, &tester));
290+
ASSERT_SUCCESS(s_s3express_tester_init(allocator));
291+
292+
struct aws_s3_tester_client_options client_options = {
293+
.part_size = MB_TO_BYTES(5),
294+
.tls_usage = AWS_S3_TLS_DISABLED,
295+
.s3express_provider_override_factory = s_s3express_credentials_provider_factory,
296+
.factory_user_data = NULL,
297+
};
298+
299+
struct aws_s3_client *client = NULL;
300+
ASSERT_SUCCESS(aws_s3_tester_client_new(&tester, &client_options, &client));
301+
302+
struct aws_byte_cursor object_path = aws_byte_cursor_from_c_str("/sse_kms");
303+
char uri[1024] = {'\0'};
304+
snprintf(uri, sizeof(uri), "" PRInSTR "sse_kms?session=", AWS_BYTE_CURSOR_PRI(g_mock_server_uri));
305+
struct aws_byte_cursor uri_cursor = aws_byte_cursor_from_c_str(uri);
306+
aws_uri_clean_up(&s_s3express_tester.mock_server);
307+
ASSERT_SUCCESS(aws_uri_init_parse(&s_s3express_tester.mock_server, allocator, &uri_cursor));
308+
309+
struct aws_s3_tester_meta_request_options put_options = {
310+
.allocator = allocator,
311+
.meta_request_type = AWS_S3_META_REQUEST_TYPE_PUT_OBJECT,
312+
.client = client,
313+
.checksum_algorithm = AWS_SCA_CRC32,
314+
.validate_get_response_checksum = false,
315+
.put_options =
316+
{
317+
.object_size_mb = 10,
318+
.object_path_override = object_path,
319+
},
320+
.mock_server = true,
321+
.use_s3express_signing = true,
322+
.sse_type = AWS_S3_TESTER_SSE_KMS,
323+
};
324+
struct aws_s3_meta_request_test_results out_results;
325+
aws_s3_meta_request_test_results_init(&out_results, allocator);
326+
ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(&tester, &put_options, &out_results));
327+
aws_s3_meta_request_test_results_clean_up(&out_results);
328+
aws_s3_client_release(client);
329+
330+
/* Call the provider shutdown callback to finish cleanup */
331+
s_s3express_tester.on_provider_shutdown_callback(s_s3express_tester.shutdown_user_data);
332+
ASSERT_SUCCESS(s_s3express_tester_cleanup());
333+
aws_s3_tester_clean_up(&tester);
334+
335+
return AWS_OP_SUCCESS;
336+
}
337+
255338
TEST_CASE(s3express_provider_get_credentials_multiple_mock_server) {
256339
(void)ctx;
257340

tests/s3_tester.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1387,6 +1387,9 @@ int aws_s3_tester_client_new(
13871387
struct aws_s3_client_config client_config = {
13881388
.part_size = options->part_size,
13891389
.max_part_size = options->max_part_size,
1390+
.s3express_provider_override_factory = options->s3express_provider_override_factory,
1391+
.factory_user_data = options->factory_user_data,
1392+
.enable_s3express = options->s3express_provider_override_factory != NULL,
13901393
};
13911394
struct aws_http_proxy_options proxy_options = {
13921395
.connection_type = AWS_HPCT_HTTP_FORWARD,

tests/s3_tester.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ struct aws_s3_tester_client_options {
140140
size_t num_network_interface_names;
141141
uint32_t setup_region : 1;
142142
uint32_t use_proxy : 1;
143+
aws_s3express_provider_factory_fn *s3express_provider_override_factory;
144+
void *factory_user_data;
143145
};
144146

145147
/* should really break this up to a client setup, and a meta_request sending */

0 commit comments

Comments
 (0)