Skip to content

Commit 50e505c

Browse files
authored
Adding user-agent information to request headers (#102)
1 parent 6855775 commit 50e505c

7 files changed

Lines changed: 354 additions & 7 deletions

File tree

include/aws/s3/private/s3_client_impl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
#include <aws/common/task_scheduler.h>
1717
#include <aws/http/connection_manager.h>
1818

19+
/* TODO automate this value in the future to prevent it from becoming out-of-sync. */
20+
#define AWS_S3_CLIENT_VERSION "0.1.x"
21+
1922
struct aws_http_connection;
2023
struct aws_http_connection_manager;
2124

include/aws/s3/private/s3_util.h

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
struct aws_allocator;
2020
struct aws_http_stream;
2121
struct aws_http_headers;
22+
struct aws_http_message;
2223
struct aws_event_loop;
2324

2425
enum aws_s3_response_status {
@@ -38,20 +39,28 @@ struct aws_cached_signing_config_aws {
3839
struct aws_signing_config_aws config;
3940
};
4041

41-
AWS_EXTERN_C_BEGIN
42-
4342
extern const struct aws_byte_cursor g_s3_service_name;
4443
extern const struct aws_byte_cursor g_range_header_name;
4544
extern const struct aws_byte_cursor g_content_range_header_name;
4645
extern const struct aws_byte_cursor g_accept_ranges_header_name;
47-
48-
AWS_S3_API
49-
extern const struct aws_byte_cursor g_acl_header_name;
50-
5146
extern const struct aws_byte_cursor g_post_method;
5247
extern const struct aws_byte_cursor g_delete_method;
5348
extern const uint32_t g_s3_max_num_upload_parts;
5449

50+
AWS_EXTERN_C_BEGIN
51+
52+
AWS_S3_API
53+
extern const struct aws_byte_cursor g_s3_client_version;
54+
55+
AWS_S3_API
56+
extern const struct aws_byte_cursor g_user_agent_header_name;
57+
58+
AWS_S3_API
59+
extern const struct aws_byte_cursor g_user_agent_header_product_name;
60+
61+
AWS_S3_API
62+
extern const struct aws_byte_cursor g_acl_header_name;
63+
5564
AWS_S3_API
5665
extern const struct aws_byte_cursor g_host_header_name;
5766

@@ -88,6 +97,9 @@ void replace_quote_entities(struct aws_allocator *allocator, struct aws_string *
8897
/* TODO could be moved to aws-c-common. */
8998
int aws_last_error_or_unknown(void);
9099

100+
AWS_S3_API
101+
void aws_s3_add_user_agent_header(struct aws_allocator *allocator, struct aws_http_message *message);
102+
91103
AWS_EXTERN_C_END
92104

93105
#endif /* AWS_S3_UTIL_H */

source/s3_meta_request.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ int aws_s3_meta_request_init_base(
123123
meta_request->initial_request_message = options->message;
124124
aws_http_message_acquire(options->message);
125125

126+
aws_s3_add_user_agent_header(meta_request->allocator, meta_request->initial_request_message);
127+
126128
if (aws_mutex_init(&meta_request->synced_data.lock)) {
127129
AWS_LOGF_ERROR(
128130
AWS_LS_S3_META_REQUEST, "id=%p Could not initialize mutex for meta request", (void *)meta_request);

source/s3_request_messages.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,8 @@ struct aws_http_message *aws_s3_message_util_copy_http_message(
427427
if (multipart_upload_ops) {
428428
if (aws_byte_cursor_eq_c_str_ignore_case(&header.name, "host") ||
429429
aws_byte_cursor_eq_c_str_ignore_case(&header.name, "x-amz-request-payer") ||
430-
aws_byte_cursor_eq_c_str_ignore_case(&header.name, "x-amz-expected-bucket-owner")) {
430+
aws_byte_cursor_eq_c_str_ignore_case(&header.name, "x-amz-expected-bucket-owner") ||
431+
aws_byte_cursor_eq_ignore_case(&header.name, &g_user_agent_header_name)) {
431432
if (aws_http_message_add_header(message, header)) {
432433
goto error_clean_up;
433434
}

source/s3_util.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
*/
55

66
#include "aws/s3/private/s3_util.h"
7+
#include "aws/s3/private/s3_client_impl.h"
78
#include <aws/auth/credentials.h>
89
#include <aws/common/string.h>
910
#include <aws/common/xml_parser.h>
1011
#include <aws/http/request_response.h>
1112
#include <aws/s3/s3.h>
1213
#include <aws/s3/s3_client.h>
1314

15+
const struct aws_byte_cursor g_s3_client_version = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL(AWS_S3_CLIENT_VERSION);
1416
const struct aws_byte_cursor g_s3_service_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("s3");
1517
const struct aws_byte_cursor g_host_header_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Host");
1618
const struct aws_byte_cursor g_range_header_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Range");
@@ -23,6 +25,10 @@ const struct aws_byte_cursor g_acl_header_name = AWS_BYTE_CUR_INIT_FROM_STRING_L
2325
const struct aws_byte_cursor g_post_method = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("POST");
2426
const struct aws_byte_cursor g_delete_method = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("DELETE");
2527

28+
const struct aws_byte_cursor g_user_agent_header_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("User-Agent");
29+
const struct aws_byte_cursor g_user_agent_header_product_name =
30+
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("CRTS3NativeClient");
31+
2632
const uint32_t g_s3_max_num_upload_parts = 10000;
2733
const size_t g_s3_min_upload_part_size = MB_TO_BYTES(5);
2834

@@ -249,3 +255,56 @@ int aws_last_error_or_unknown() {
249255

250256
return error;
251257
}
258+
259+
void aws_s3_add_user_agent_header(struct aws_allocator *allocator, struct aws_http_message *message) {
260+
AWS_PRECONDITION(allocator);
261+
AWS_PRECONDITION(message);
262+
263+
const struct aws_byte_cursor space_delimeter = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL(" ");
264+
const struct aws_byte_cursor forward_slash = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("/");
265+
266+
const size_t user_agent_product_version_length =
267+
g_user_agent_header_product_name.len + forward_slash.len + g_s3_client_version.len;
268+
269+
struct aws_http_headers *headers = aws_http_message_get_headers(message);
270+
AWS_ASSERT(headers != NULL);
271+
272+
struct aws_byte_cursor current_user_agent_header;
273+
AWS_ZERO_STRUCT(current_user_agent_header);
274+
275+
struct aws_byte_buf user_agent_buffer;
276+
AWS_ZERO_STRUCT(user_agent_buffer);
277+
278+
if (!aws_http_headers_get(headers, g_user_agent_header_name, &current_user_agent_header)) {
279+
/* If the header was found, then create a buffer with the total size we'll need, and append the curent user
280+
* agent header with a trailing space. */
281+
aws_byte_buf_init(
282+
&user_agent_buffer,
283+
allocator,
284+
current_user_agent_header.len + space_delimeter.len + user_agent_product_version_length);
285+
286+
aws_byte_buf_append_dynamic(&user_agent_buffer, &current_user_agent_header);
287+
288+
aws_byte_buf_append_dynamic(&user_agent_buffer, &space_delimeter);
289+
290+
} else {
291+
AWS_ASSERT(aws_last_error() == AWS_ERROR_HTTP_HEADER_NOT_FOUND);
292+
293+
/* If the header was not found, then create a buffer with just the size of the user agent string that is about
294+
* to be appended to the buffer. */
295+
aws_byte_buf_init(&user_agent_buffer, allocator, user_agent_product_version_length);
296+
}
297+
298+
/* Append the client's user-agent string. */
299+
{
300+
aws_byte_buf_append_dynamic(&user_agent_buffer, &g_user_agent_header_product_name);
301+
aws_byte_buf_append_dynamic(&user_agent_buffer, &forward_slash);
302+
aws_byte_buf_append_dynamic(&user_agent_buffer, &g_s3_client_version);
303+
}
304+
305+
/* Apply the updated header. */
306+
aws_http_headers_set(headers, g_user_agent_header_name, aws_byte_cursor_from_buf(&user_agent_buffer));
307+
308+
/* Clean up the scratch buffer. */
309+
aws_byte_buf_clean_up(&user_agent_buffer);
310+
}

tests/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ add_net_test_case(test_s3_bad_endpoint)
6464
add_net_test_case(test_s3_put_object_clamp_part_size)
6565
add_net_test_case(test_s3_replace_quote_entities)
6666
add_net_test_case(test_s3_different_endpoints)
67+
add_test_case(test_add_user_agent_header)
68+
add_net_test_case(test_s3_auto_ranged_get_sending_user_agent)
69+
add_net_test_case(test_s3_auto_ranged_put_sending_user_agent)
70+
add_net_test_case(test_s3_default_sending_meta_request)
6771

6872
add_test_case(test_get_existing_compute_platform_info)
6973
add_test_case(test_get_nonexistent_compute_platform_info)

0 commit comments

Comments
 (0)