Skip to content

Commit b19d924

Browse files
authored
Fix handling of content-encoding header for default request (#286)
1 parent b515b06 commit b19d924

File tree

5 files changed

+188
-3
lines changed

5 files changed

+188
-3
lines changed

source/s3_request_messages.c

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,8 @@ struct aws_input_stream *aws_s3_message_util_assign_body(
745745
}
746746

747747
struct aws_input_stream *input_stream = aws_input_stream_new_from_cursor(allocator, &buffer_byte_cursor);
748+
struct aws_byte_buf content_encoding_header_buf;
749+
AWS_ZERO_STRUCT(content_encoding_header_buf);
748750

749751
if (input_stream == NULL) {
750752
goto error_clean_up;
@@ -754,11 +756,31 @@ struct aws_input_stream *aws_s3_message_util_assign_body(
754756
if (checksum_config->location == AWS_SCL_TRAILER) {
755757
/* aws-chunked encode the payload and add related headers */
756758

757-
/* set Content-Encoding header. TODO: the aws-chunked should be appended to the existing content encoding.
759+
/* set Content-Encoding header. If the header already exists, append the exisiting value to aws-chunked
760+
* We already made sure that the existing value is not 'aws_chunked' in 'aws_s3_client_make_meta_request'
761+
* function.
758762
*/
759-
if (aws_http_headers_set(headers, g_content_encoding_header_name, g_content_encoding_header_aws_chunked)) {
763+
struct aws_byte_cursor content_encoding_header_cursor;
764+
bool has_content_encoding_header =
765+
aws_http_headers_get(headers, g_content_encoding_header_name, &content_encoding_header_cursor) ==
766+
AWS_OP_SUCCESS;
767+
size_t content_encoding_header_buf_size =
768+
has_content_encoding_header
769+
? g_content_encoding_header_aws_chunked.len + content_encoding_header_cursor.len + 1
770+
: g_content_encoding_header_aws_chunked.len;
771+
aws_byte_buf_init(&content_encoding_header_buf, allocator, content_encoding_header_buf_size);
772+
773+
if (has_content_encoding_header) {
774+
aws_byte_buf_append_dynamic(&content_encoding_header_buf, &content_encoding_header_cursor);
775+
aws_byte_buf_append_byte_dynamic(&content_encoding_header_buf, ',');
776+
}
777+
aws_byte_buf_append_dynamic(&content_encoding_header_buf, &g_content_encoding_header_aws_chunked);
778+
779+
if (aws_http_headers_set(
780+
headers, g_content_encoding_header_name, aws_byte_cursor_from_buf(&content_encoding_header_buf))) {
760781
goto error_clean_up;
761782
}
783+
762784
/* set x-amz-trailer header */
763785
if (aws_http_headers_set(
764786
headers,
@@ -803,12 +825,13 @@ struct aws_input_stream *aws_s3_message_util_assign_body(
803825
aws_http_message_set_body_stream(out_message, input_stream);
804826
/* Let the message take the full ownership */
805827
aws_input_stream_release(input_stream);
806-
828+
aws_byte_buf_clean_up(&content_encoding_header_buf);
807829
return input_stream;
808830

809831
error_clean_up:
810832
AWS_LOGF_ERROR(AWS_LS_S3_CLIENT, "Failed to assign body for s3 request http message, from body buffer .");
811833
aws_input_stream_release(input_stream);
834+
aws_byte_buf_clean_up(&content_encoding_header_buf);
812835
return NULL;
813836
}
814837

tests/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ add_net_test_case(test_s3_put_object_tls_default)
7777
add_net_test_case(test_s3_multipart_put_object_with_acl)
7878
add_net_test_case(test_s3_put_object_multiple)
7979
add_net_test_case(test_s3_put_object_less_than_part_size)
80+
add_net_test_case(test_s3_put_object_less_than_part_size_with_content_encoding)
81+
add_net_test_case(test_s3_put_object_mpu_with_content_encoding)
8082
add_net_test_case(test_s3_put_object_multipart_threshold)
8183
add_net_test_case(test_s3_put_object_multipart_threshold_less_than_part_size)
8284
add_net_test_case(test_s3_put_object_empty_object)

tests/s3_data_plane_tests.c

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1825,6 +1825,157 @@ static int s_test_s3_put_object_less_than_part_size(struct aws_allocator *alloca
18251825
return 0;
18261826
}
18271827

1828+
AWS_TEST_CASE(
1829+
test_s3_put_object_less_than_part_size_with_content_encoding,
1830+
s_test_s3_put_object_less_than_part_size_with_content_encoding)
1831+
static int s_test_s3_put_object_less_than_part_size_with_content_encoding(struct aws_allocator *allocator, void *ctx) {
1832+
(void)ctx;
1833+
1834+
struct aws_s3_tester tester;
1835+
ASSERT_SUCCESS(aws_s3_tester_init(allocator, &tester));
1836+
1837+
struct aws_s3_client_config client_config = {
1838+
.part_size = 20 * 1024 * 1024,
1839+
};
1840+
1841+
ASSERT_SUCCESS(aws_s3_tester_bind_client(
1842+
&tester, &client_config, AWS_S3_TESTER_BIND_CLIENT_REGION | AWS_S3_TESTER_BIND_CLIENT_SIGNING));
1843+
1844+
struct aws_s3_client *client = aws_s3_client_new(allocator, &client_config);
1845+
1846+
ASSERT_TRUE(client != NULL);
1847+
1848+
struct aws_byte_cursor content_encoding_cursor = aws_byte_cursor_from_c_str("gzip");
1849+
uint32_t object_size_mb = 1;
1850+
char object_path_sprintf_buffer[128] = "";
1851+
snprintf(
1852+
object_path_sprintf_buffer,
1853+
sizeof(object_path_sprintf_buffer),
1854+
"" PRInSTR "-content-encoding-%uMB.txt",
1855+
AWS_BYTE_CURSOR_PRI(g_put_object_prefix),
1856+
object_size_mb);
1857+
struct aws_byte_cursor object_path_cursor = aws_byte_cursor_from_c_str(object_path_sprintf_buffer);
1858+
1859+
/*** put file with encoding ***/
1860+
struct aws_s3_tester_meta_request_options put_options = {
1861+
.allocator = allocator,
1862+
.meta_request_type = AWS_S3_META_REQUEST_TYPE_PUT_OBJECT,
1863+
.client = client,
1864+
.checksum_algorithm = AWS_SCA_SHA256,
1865+
.put_options =
1866+
{
1867+
.object_size_mb = object_size_mb,
1868+
.object_path_override = object_path_cursor,
1869+
.content_encoding = content_encoding_cursor,
1870+
},
1871+
};
1872+
1873+
ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(&tester, &put_options, NULL));
1874+
1875+
/*** get file and validate encoding ***/
1876+
struct aws_s3_tester_meta_request_options get_options = {
1877+
.allocator = allocator,
1878+
.meta_request_type = AWS_S3_META_REQUEST_TYPE_GET_OBJECT,
1879+
.validate_type = AWS_S3_TESTER_VALIDATE_TYPE_EXPECT_SUCCESS,
1880+
.client = client,
1881+
.get_options =
1882+
{
1883+
.object_path = object_path_cursor,
1884+
},
1885+
};
1886+
1887+
struct aws_s3_meta_request_test_results get_object_result;
1888+
aws_s3_meta_request_test_results_init(&get_object_result, allocator);
1889+
1890+
ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(&tester, &get_options, &get_object_result));
1891+
struct aws_byte_cursor content_encoding_header_cursor;
1892+
ASSERT_SUCCESS(aws_http_headers_get(
1893+
get_object_result.response_headers, g_content_encoding_header_name, &content_encoding_header_cursor));
1894+
ASSERT_TRUE(aws_byte_cursor_eq(&content_encoding_cursor, &content_encoding_header_cursor));
1895+
aws_s3_meta_request_test_results_clean_up(&get_object_result);
1896+
1897+
client = aws_s3_client_release(client);
1898+
1899+
aws_s3_tester_clean_up(&tester);
1900+
1901+
return 0;
1902+
}
1903+
1904+
AWS_TEST_CASE(test_s3_put_object_mpu_with_content_encoding, s_test_s3_put_object_mpu_with_content_encoding)
1905+
static int s_test_s3_put_object_mpu_with_content_encoding(struct aws_allocator *allocator, void *ctx) {
1906+
(void)ctx;
1907+
1908+
struct aws_s3_tester tester;
1909+
ASSERT_SUCCESS(aws_s3_tester_init(allocator, &tester));
1910+
1911+
struct aws_s3_client_config client_config = {
1912+
.part_size = 5 * 1024 * 1024,
1913+
};
1914+
1915+
ASSERT_SUCCESS(aws_s3_tester_bind_client(
1916+
&tester, &client_config, AWS_S3_TESTER_BIND_CLIENT_REGION | AWS_S3_TESTER_BIND_CLIENT_SIGNING));
1917+
1918+
struct aws_s3_client *client = aws_s3_client_new(allocator, &client_config);
1919+
1920+
ASSERT_TRUE(client != NULL);
1921+
1922+
struct aws_byte_cursor content_encoding_cursor = aws_byte_cursor_from_c_str("gzip");
1923+
uint32_t object_size_mb = 10;
1924+
char object_path_sprintf_buffer[128] = "";
1925+
snprintf(
1926+
object_path_sprintf_buffer,
1927+
sizeof(object_path_sprintf_buffer),
1928+
"" PRInSTR "-content-encoding-%uMB.txt",
1929+
AWS_BYTE_CURSOR_PRI(g_put_object_prefix),
1930+
object_size_mb);
1931+
struct aws_byte_cursor object_path_cursor = aws_byte_cursor_from_c_str(object_path_sprintf_buffer);
1932+
1933+
/*** put file with encoding ***/
1934+
struct aws_s3_tester_meta_request_options put_options = {
1935+
.allocator = allocator,
1936+
.meta_request_type = AWS_S3_META_REQUEST_TYPE_PUT_OBJECT,
1937+
.client = client,
1938+
.checksum_algorithm = AWS_SCA_SHA256,
1939+
.put_options =
1940+
{
1941+
.object_size_mb = object_size_mb,
1942+
.object_path_override = object_path_cursor,
1943+
.content_encoding = content_encoding_cursor,
1944+
.ensure_multipart = true,
1945+
},
1946+
};
1947+
1948+
ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(&tester, &put_options, NULL));
1949+
1950+
/*** get file and validate encoding ***/
1951+
struct aws_s3_tester_meta_request_options get_options = {
1952+
.allocator = allocator,
1953+
.meta_request_type = AWS_S3_META_REQUEST_TYPE_GET_OBJECT,
1954+
.validate_type = AWS_S3_TESTER_VALIDATE_TYPE_EXPECT_SUCCESS,
1955+
.client = client,
1956+
.get_options =
1957+
{
1958+
.object_path = object_path_cursor,
1959+
},
1960+
};
1961+
1962+
struct aws_s3_meta_request_test_results get_object_result;
1963+
aws_s3_meta_request_test_results_init(&get_object_result, allocator);
1964+
1965+
ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(&tester, &get_options, &get_object_result));
1966+
struct aws_byte_cursor content_encoding_header_cursor;
1967+
ASSERT_SUCCESS(aws_http_headers_get(
1968+
get_object_result.response_headers, g_content_encoding_header_name, &content_encoding_header_cursor));
1969+
ASSERT_TRUE(aws_byte_cursor_eq(&content_encoding_cursor, &content_encoding_header_cursor));
1970+
aws_s3_meta_request_test_results_clean_up(&get_object_result);
1971+
1972+
client = aws_s3_client_release(client);
1973+
1974+
aws_s3_tester_clean_up(&tester);
1975+
1976+
return 0;
1977+
}
1978+
18281979
AWS_TEST_CASE(test_s3_put_object_multipart_threshold, s_test_s3_put_object_multipart_threshold)
18291980
static int s_test_s3_put_object_multipart_threshold(struct aws_allocator *allocator, void *ctx) {
18301981
(void)ctx;

tests/s3_tester.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,6 +1497,14 @@ int aws_s3_tester_send_meta_request_with_options(
14971497
aws_http_message_set_body_stream(message, NULL);
14981498
}
14991499

1500+
if (options->put_options.content_encoding.ptr != NULL) {
1501+
struct aws_http_header content_encoding_header = {
1502+
.name = g_content_encoding_header_name,
1503+
.value = options->put_options.content_encoding,
1504+
};
1505+
aws_http_message_add_header(message, content_encoding_header);
1506+
}
1507+
15001508
meta_request_options.message = message;
15011509
aws_byte_buf_clean_up(&object_path_buffer);
15021510
}

tests/s3_tester.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ struct aws_s3_tester_meta_request_options {
187187
struct aws_s3_meta_request_resume_token *resume_token;
188188
/* manually overwrite the content length for some invalid input stream */
189189
size_t content_length;
190+
struct aws_byte_cursor content_encoding;
190191
} put_options;
191192

192193
enum aws_s3_tester_sse_type sse_type;

0 commit comments

Comments
 (0)