|
18 | 18 | #include <aws/auth/signing_result.h> |
19 | 19 | #include <aws/common/clock.h> |
20 | 20 | #include <aws/common/encoding.h> |
| 21 | +#include <aws/common/file.h> |
21 | 22 | #include <aws/common/string.h> |
22 | 23 | #include <aws/common/system_info.h> |
23 | 24 | #include <aws/io/async_stream.h> |
24 | 25 | #include <aws/io/event_loop.h> |
25 | 26 | #include <aws/io/retry_strategy.h> |
26 | 27 | #include <aws/io/socket.h> |
27 | 28 | #include <aws/io/stream.h> |
| 29 | +#include <errno.h> |
28 | 30 | #include <inttypes.h> |
29 | 31 |
|
30 | 32 | static const size_t s_dynamic_body_initial_buf_size = KB_TO_BYTES(1); |
@@ -232,6 +234,57 @@ int aws_s3_meta_request_init_base( |
232 | 234 | /* Keep original message around, for headers, method, and synchronous body-stream (if any) */ |
233 | 235 | meta_request->initial_request_message = aws_http_message_acquire(options->message); |
234 | 236 |
|
| 237 | + if (options->recv_filepath.len > 0) { |
| 238 | + |
| 239 | + meta_request->recv_filepath = aws_string_new_from_cursor(allocator, &options->recv_filepath); |
| 240 | + switch (options->recv_file_option) { |
| 241 | + case AWS_S3_RECV_FILE_CREATE_OR_REPLACE: |
| 242 | + meta_request->recv_file = aws_fopen(aws_string_c_str(meta_request->recv_filepath), "wb"); |
| 243 | + break; |
| 244 | + |
| 245 | + case AWS_S3_RECV_FILE_CREATE_NEW: |
| 246 | + if (aws_path_exists(meta_request->recv_filepath)) { |
| 247 | + AWS_LOGF_ERROR( |
| 248 | + AWS_LS_S3_META_REQUEST, |
| 249 | + "id=%p Cannot receive file via CREATE_NEW: file already exists", |
| 250 | + (void *)meta_request); |
| 251 | + aws_raise_error(AWS_ERROR_S3_RECV_FILE_ALREADY_EXISTS); |
| 252 | + break; |
| 253 | + } else { |
| 254 | + meta_request->recv_file = aws_fopen(aws_string_c_str(meta_request->recv_filepath), "wb"); |
| 255 | + break; |
| 256 | + } |
| 257 | + case AWS_S3_RECV_FILE_CREATE_OR_APPEND: |
| 258 | + meta_request->recv_file = aws_fopen(aws_string_c_str(meta_request->recv_filepath), "ab"); |
| 259 | + break; |
| 260 | + case AWS_S3_RECV_FILE_WRITE_TO_POSITION: |
| 261 | + if (!aws_path_exists(meta_request->recv_filepath)) { |
| 262 | + AWS_LOGF_ERROR( |
| 263 | + AWS_LS_S3_META_REQUEST, |
| 264 | + "id=%p Cannot receive file via WRITE_TO_POSITION: file not found.", |
| 265 | + (void *)meta_request); |
| 266 | + aws_raise_error(AWS_ERROR_S3_RECV_FILE_NOT_FOUND); |
| 267 | + break; |
| 268 | + } else { |
| 269 | + meta_request->recv_file = aws_fopen(aws_string_c_str(meta_request->recv_filepath), "r+"); |
| 270 | + if (meta_request->recv_file && |
| 271 | + aws_fseek(meta_request->recv_file, options->recv_file_position, SEEK_SET) != AWS_OP_SUCCESS) { |
| 272 | + /* error out. */ |
| 273 | + goto error; |
| 274 | + } |
| 275 | + break; |
| 276 | + } |
| 277 | + |
| 278 | + default: |
| 279 | + AWS_ASSERT(false); |
| 280 | + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); |
| 281 | + break; |
| 282 | + } |
| 283 | + if (!meta_request->recv_file) { |
| 284 | + goto error; |
| 285 | + } |
| 286 | + } |
| 287 | + |
235 | 288 | /* If the request's body is being passed in some other way, set that up. |
236 | 289 | * (we checked earlier that the request body is not being passed multiple ways) */ |
237 | 290 | if (options->send_filepath.len > 0) { |
@@ -440,6 +493,15 @@ static void s_s3_meta_request_destroy(void *user_data) { |
440 | 493 | /* endpoint should have already been released and set NULL by the meta request finish call. |
441 | 494 | * But call release() again, just in case we're tearing down a half-initialized meta request */ |
442 | 495 | aws_s3_endpoint_release(meta_request->endpoint); |
| 496 | + if (meta_request->recv_file) { |
| 497 | + fclose(meta_request->recv_file); |
| 498 | + meta_request->recv_file = NULL; |
| 499 | + if (meta_request->recv_file_delete_on_failure) { |
| 500 | + /* If the meta request succeed, the file should be closed from finish call. So it must be failing. */ |
| 501 | + aws_file_delete(meta_request->recv_filepath); |
| 502 | + } |
| 503 | + } |
| 504 | + aws_string_destroy(meta_request->recv_filepath); |
443 | 505 |
|
444 | 506 | /* Client may be NULL if meta request failed mid-creation (or this some weird testing mock with no client) */ |
445 | 507 | if (meta_request->client != NULL) { |
@@ -1779,19 +1841,47 @@ static void s_s3_meta_request_event_delivery_task(struct aws_task *task, void *a |
1779 | 1841 |
|
1780 | 1842 | if (error_code == AWS_ERROR_SUCCESS && response_body.len > 0) { |
1781 | 1843 | if (meta_request->meta_request_level_running_response_sum) { |
1782 | | - aws_checksum_update(meta_request->meta_request_level_running_response_sum, &response_body); |
| 1844 | + if (aws_checksum_update( |
| 1845 | + meta_request->meta_request_level_running_response_sum, &response_body)) { |
| 1846 | + error_code = aws_last_error(); |
| 1847 | + AWS_LOGF_ERROR( |
| 1848 | + AWS_LS_S3_META_REQUEST, |
| 1849 | + "id=%p Failed to update checksum. last error:%s", |
| 1850 | + (void *)meta_request, |
| 1851 | + aws_error_name(error_code)); |
| 1852 | + } |
1783 | 1853 | } |
1784 | | - if (meta_request->body_callback != NULL && |
1785 | | - meta_request->body_callback( |
1786 | | - meta_request, &response_body, request->part_range_start, meta_request->user_data)) { |
1787 | | - |
1788 | | - error_code = aws_last_error_or_unknown(); |
1789 | | - AWS_LOGF_ERROR( |
1790 | | - AWS_LS_S3_META_REQUEST, |
1791 | | - "id=%p Response body callback raised error %d (%s).", |
1792 | | - (void *)meta_request, |
1793 | | - error_code, |
1794 | | - aws_error_str(error_code)); |
| 1854 | + if (error_code == AWS_ERROR_SUCCESS) { |
| 1855 | + if (meta_request->recv_file) { |
| 1856 | + /* Write the data directly to the file. No need to seek, since the event will always be |
| 1857 | + * delivered with the right order. */ |
| 1858 | + if (fwrite((void *)response_body.ptr, response_body.len, 1, meta_request->recv_file) < 1) { |
| 1859 | + int errno_value = ferror(meta_request->recv_file) ? errno : 0; /* Always cache errno */ |
| 1860 | + aws_translate_and_raise_io_error_or(errno_value, AWS_ERROR_FILE_WRITE_FAILURE); |
| 1861 | + error_code = aws_last_error(); |
| 1862 | + AWS_LOGF_ERROR( |
| 1863 | + AWS_LS_S3_META_REQUEST, |
| 1864 | + "id=%p Failed writing to file. errno:%d. aws-error:%s", |
| 1865 | + (void *)meta_request, |
| 1866 | + errno_value, |
| 1867 | + aws_error_name(error_code)); |
| 1868 | + } |
| 1869 | + if (meta_request->client->enable_read_backpressure) { |
| 1870 | + aws_s3_meta_request_increment_read_window(meta_request, response_body.len); |
| 1871 | + } |
| 1872 | + } else if ( |
| 1873 | + meta_request->body_callback != NULL && |
| 1874 | + meta_request->body_callback( |
| 1875 | + meta_request, &response_body, request->part_range_start, meta_request->user_data)) { |
| 1876 | + |
| 1877 | + error_code = aws_last_error_or_unknown(); |
| 1878 | + AWS_LOGF_ERROR( |
| 1879 | + AWS_LS_S3_META_REQUEST, |
| 1880 | + "id=%p Response body callback raised error %d (%s).", |
| 1881 | + (void *)meta_request, |
| 1882 | + error_code, |
| 1883 | + aws_error_str(error_code)); |
| 1884 | + } |
1795 | 1885 | } |
1796 | 1886 | } |
1797 | 1887 | aws_atomic_fetch_sub(&client->stats.num_requests_streaming_response, 1); |
@@ -1979,6 +2069,14 @@ void aws_s3_meta_request_finish_default(struct aws_s3_meta_request *meta_request |
1979 | 2069 | pending_async_write_waker(pending_async_write_waker_user_data); |
1980 | 2070 | } |
1981 | 2071 |
|
| 2072 | + if (meta_request->recv_file) { |
| 2073 | + fclose(meta_request->recv_file); |
| 2074 | + meta_request->recv_file = NULL; |
| 2075 | + if (finish_result.error_code && meta_request->recv_file_delete_on_failure) { |
| 2076 | + aws_file_delete(meta_request->recv_filepath); |
| 2077 | + } |
| 2078 | + } |
| 2079 | + |
1982 | 2080 | while (!aws_linked_list_empty(&release_request_list)) { |
1983 | 2081 | struct aws_linked_list_node *request_node = aws_linked_list_pop_front(&release_request_list); |
1984 | 2082 | struct aws_s3_request *release_request = AWS_CONTAINER_OF(request_node, struct aws_s3_request, node); |
|
0 commit comments