s3_auto_ranged_get.c 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. #include "aws/s3/private/s3_auto_ranged_get.h"
  6. #include "aws/s3/private/s3_client_impl.h"
  7. #include "aws/s3/private/s3_meta_request_impl.h"
  8. #include "aws/s3/private/s3_request_messages.h"
  9. #include "aws/s3/private/s3_util.h"
  10. #include <aws/common/string.h>
  11. #include <inttypes.h>
  12. #ifdef _MSC_VER
  13. /* sscanf warning (not currently scanning for strings) */
  14. # pragma warning(disable : 4996)
  15. #endif
  16. const uint32_t s_conservative_max_requests_in_flight = 8;
  17. const struct aws_byte_cursor g_application_xml_value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("application/xml");
  18. const struct aws_byte_cursor g_object_size_value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("ActualObjectSize");
  19. static void s_s3_meta_request_auto_ranged_get_destroy(struct aws_s3_meta_request *meta_request);
  20. static bool s_s3_auto_ranged_get_update(
  21. struct aws_s3_meta_request *meta_request,
  22. uint32_t flags,
  23. struct aws_s3_request **out_request);
  24. static int s_s3_auto_ranged_get_prepare_request(
  25. struct aws_s3_meta_request *meta_request,
  26. struct aws_s3_request *request);
  27. static void s_s3_auto_ranged_get_request_finished(
  28. struct aws_s3_meta_request *meta_request,
  29. struct aws_s3_request *request,
  30. int error_code);
  31. static struct aws_s3_meta_request_vtable s_s3_auto_ranged_get_vtable = {
  32. .update = s_s3_auto_ranged_get_update,
  33. .send_request_finish = aws_s3_meta_request_send_request_finish_default,
  34. .prepare_request = s_s3_auto_ranged_get_prepare_request,
  35. .init_signing_date_time = aws_s3_meta_request_init_signing_date_time_default,
  36. .sign_request = aws_s3_meta_request_sign_request_default,
  37. .finished_request = s_s3_auto_ranged_get_request_finished,
  38. .destroy = s_s3_meta_request_auto_ranged_get_destroy,
  39. .finish = aws_s3_meta_request_finish_default,
  40. };
  41. static int s_s3_auto_ranged_get_success_status(struct aws_s3_meta_request *meta_request) {
  42. AWS_PRECONDITION(meta_request);
  43. struct aws_s3_auto_ranged_get *auto_ranged_get = meta_request->impl;
  44. AWS_PRECONDITION(auto_ranged_get);
  45. if (auto_ranged_get->initial_message_has_range_header) {
  46. return AWS_S3_RESPONSE_STATUS_RANGE_SUCCESS;
  47. }
  48. return AWS_S3_RESPONSE_STATUS_SUCCESS;
  49. }
  50. /* Allocate a new auto-ranged-get meta request. */
  51. struct aws_s3_meta_request *aws_s3_meta_request_auto_ranged_get_new(
  52. struct aws_allocator *allocator,
  53. struct aws_s3_client *client,
  54. size_t part_size,
  55. const struct aws_s3_meta_request_options *options) {
  56. AWS_PRECONDITION(allocator);
  57. AWS_PRECONDITION(client);
  58. AWS_PRECONDITION(options);
  59. AWS_PRECONDITION(options->message);
  60. struct aws_s3_auto_ranged_get *auto_ranged_get =
  61. aws_mem_calloc(allocator, 1, sizeof(struct aws_s3_auto_ranged_get));
  62. /* Try to initialize the base type. */
  63. if (aws_s3_meta_request_init_base(
  64. allocator,
  65. client,
  66. part_size,
  67. false,
  68. options,
  69. auto_ranged_get,
  70. &s_s3_auto_ranged_get_vtable,
  71. &auto_ranged_get->base)) {
  72. AWS_LOGF_ERROR(
  73. AWS_LS_S3_META_REQUEST,
  74. "id=%p Could not initialize base type for Auto-Ranged-Get Meta Request.",
  75. (void *)auto_ranged_get);
  76. aws_mem_release(allocator, auto_ranged_get);
  77. return NULL;
  78. }
  79. struct aws_http_headers *headers = aws_http_message_get_headers(auto_ranged_get->base.initial_request_message);
  80. AWS_ASSERT(headers != NULL);
  81. auto_ranged_get->initial_message_has_range_header = aws_http_headers_has(headers, g_range_header_name);
  82. auto_ranged_get->initial_message_has_if_match_header = aws_http_headers_has(headers, g_if_match_header_name);
  83. AWS_LOGF_DEBUG(
  84. AWS_LS_S3_META_REQUEST, "id=%p Created new Auto-Ranged Get Meta Request.", (void *)&auto_ranged_get->base);
  85. return &auto_ranged_get->base;
  86. }
  87. static void s_s3_meta_request_auto_ranged_get_destroy(struct aws_s3_meta_request *meta_request) {
  88. AWS_PRECONDITION(meta_request);
  89. AWS_PRECONDITION(meta_request->impl);
  90. struct aws_s3_auto_ranged_get *auto_ranged_get = meta_request->impl;
  91. aws_string_destroy(auto_ranged_get->etag);
  92. aws_mem_release(meta_request->allocator, auto_ranged_get);
  93. }
  94. static bool s_s3_auto_ranged_get_update(
  95. struct aws_s3_meta_request *meta_request,
  96. uint32_t flags,
  97. struct aws_s3_request **out_request) {
  98. AWS_PRECONDITION(meta_request);
  99. AWS_PRECONDITION(out_request);
  100. struct aws_s3_auto_ranged_get *auto_ranged_get = meta_request->impl;
  101. struct aws_s3_request *request = NULL;
  102. bool work_remaining = false;
  103. /* BEGIN CRITICAL SECTION */
  104. {
  105. aws_s3_meta_request_lock_synced_data(meta_request);
  106. /* If nothing has set the "finish result" then this meta request is still in progress, and we can potentially
  107. * send additional requests. */
  108. if (!aws_s3_meta_request_has_finish_result_synced(meta_request)) {
  109. if ((flags & AWS_S3_META_REQUEST_UPDATE_FLAG_CONSERVATIVE) != 0) {
  110. uint32_t num_requests_in_flight =
  111. (auto_ranged_get->synced_data.num_parts_requested -
  112. auto_ranged_get->synced_data.num_parts_completed) +
  113. (uint32_t)aws_priority_queue_size(&meta_request->synced_data.pending_body_streaming_requests);
  114. /* auto-ranged-gets make use of body streaming, which will hold onto response bodies if parts earlier in
  115. * the file haven't arrived yet. This can potentially create a lot of backed up requests, causing us to
  116. * hit our global request limit. To help mitigate this, when the "conservative" flag is passed in, we
  117. * only allow the total amount of requests being sent/streamed to be inside of a set limit. */
  118. if (num_requests_in_flight > s_conservative_max_requests_in_flight) {
  119. goto has_work_remaining;
  120. }
  121. }
  122. /* If the overall range of the object that we are trying to retrieve isn't known yet, then we need to send a
  123. * request to figure that out. */
  124. if (!auto_ranged_get->synced_data.object_range_known) {
  125. /* If there exists a range header or we require validation of the response checksum, we currently always
  126. * do a head request first.
  127. * S3 returns the checksum of the entire object from the HEAD response
  128. *
  129. * For the range header value could be parsed client-side, doing so presents a number of
  130. * complications. For example, the given range could be an unsatisfiable range, and might not even
  131. * specify a complete range. To keep things simple, we are currently relying on the service to handle
  132. * turning the Range header into a Content-Range response header.*/
  133. bool head_object_required = auto_ranged_get->initial_message_has_range_header != 0 ||
  134. meta_request->checksum_config.validate_response_checksum;
  135. if (head_object_required) {
  136. /* If the head object request hasn't been sent yet, then send it now. */
  137. if (!auto_ranged_get->synced_data.head_object_sent) {
  138. request = aws_s3_request_new(
  139. meta_request,
  140. AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_HEAD_OBJECT,
  141. 0,
  142. AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS | AWS_S3_REQUEST_FLAG_PART_SIZE_RESPONSE_BODY);
  143. request->discovers_object_size = true;
  144. auto_ranged_get->synced_data.head_object_sent = true;
  145. }
  146. } else if (auto_ranged_get->synced_data.num_parts_requested == 0) {
  147. /* If we aren't using a head object, then discover the size of the object while trying to get the
  148. * first part. */
  149. request = aws_s3_request_new(
  150. meta_request,
  151. AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_PART,
  152. 1,
  153. AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS | AWS_S3_REQUEST_FLAG_PART_SIZE_RESPONSE_BODY);
  154. request->part_range_start = 0;
  155. request->part_range_end = meta_request->part_size - 1;
  156. request->discovers_object_size = true;
  157. ++auto_ranged_get->synced_data.num_parts_requested;
  158. }
  159. goto has_work_remaining;
  160. }
  161. /* If the object range is known and that range is empty, then we have an empty file to request. */
  162. if (auto_ranged_get->synced_data.object_range_start == 0 &&
  163. auto_ranged_get->synced_data.object_range_end == 0) {
  164. if (auto_ranged_get->synced_data.get_without_range_sent) {
  165. if (auto_ranged_get->synced_data.get_without_range_completed) {
  166. goto no_work_remaining;
  167. } else {
  168. goto has_work_remaining;
  169. }
  170. }
  171. request = aws_s3_request_new(
  172. meta_request,
  173. AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_INITIAL_MESSAGE,
  174. 0,
  175. AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS);
  176. auto_ranged_get->synced_data.get_without_range_sent = true;
  177. goto has_work_remaining;
  178. }
  179. /* If there are still more parts to be requested */
  180. if (auto_ranged_get->synced_data.num_parts_requested < auto_ranged_get->synced_data.total_num_parts) {
  181. if (meta_request->client->enable_read_backpressure) {
  182. /* Don't start a part until we have enough window to send bytes to the user.
  183. *
  184. * Note that we start a part once we have enough window to deliver ANY of its bytes.
  185. * If we waited until the window was large enough for the WHOLE part,
  186. * we could end up stuck in a situation where the user is
  187. * waiting for more bytes before they'll open the window,
  188. * and this implementation is waiting for more window before it will send more parts. */
  189. uint64_t read_data_requested =
  190. auto_ranged_get->synced_data.num_parts_requested * meta_request->part_size;
  191. if (read_data_requested >= meta_request->synced_data.read_window_running_total) {
  192. /* Avoid spamming users with this DEBUG message */
  193. if (auto_ranged_get->synced_data.read_window_warning_issued == 0) {
  194. auto_ranged_get->synced_data.read_window_warning_issued = 1;
  195. AWS_LOGF_DEBUG(
  196. AWS_LS_S3_META_REQUEST,
  197. "id=%p: Download paused because read window is zero. "
  198. "You must increment to window to continue.",
  199. (void *)meta_request);
  200. }
  201. goto has_work_remaining;
  202. }
  203. auto_ranged_get->synced_data.read_window_warning_issued = 0;
  204. }
  205. request = aws_s3_request_new(
  206. meta_request,
  207. AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_PART,
  208. auto_ranged_get->synced_data.num_parts_requested + 1,
  209. AWS_S3_REQUEST_FLAG_PART_SIZE_RESPONSE_BODY);
  210. aws_s3_get_part_range(
  211. auto_ranged_get->synced_data.object_range_start,
  212. auto_ranged_get->synced_data.object_range_end,
  213. meta_request->part_size,
  214. request->part_number,
  215. &request->part_range_start,
  216. &request->part_range_end);
  217. ++auto_ranged_get->synced_data.num_parts_requested;
  218. goto has_work_remaining;
  219. }
  220. /* If there are parts that have not attempted delivery to the caller, then there is still work being done.
  221. */
  222. if (meta_request->synced_data.num_parts_delivery_completed < auto_ranged_get->synced_data.total_num_parts) {
  223. goto has_work_remaining;
  224. }
  225. } else {
  226. /* Else, if there is a finish result set, make sure that all work-in-progress winds down before the meta
  227. * request completely exits. */
  228. if (auto_ranged_get->synced_data.head_object_sent && !auto_ranged_get->synced_data.head_object_completed) {
  229. goto has_work_remaining;
  230. }
  231. /* Wait for all requests to complete (successfully or unsuccessfully) before finishing.*/
  232. if (auto_ranged_get->synced_data.num_parts_completed < auto_ranged_get->synced_data.num_parts_requested) {
  233. goto has_work_remaining;
  234. }
  235. if (auto_ranged_get->synced_data.get_without_range_sent &&
  236. !auto_ranged_get->synced_data.get_without_range_completed) {
  237. goto has_work_remaining;
  238. }
  239. /* If some parts are still being delivered to the caller, then wait for those to finish. */
  240. if (meta_request->synced_data.num_parts_delivery_completed <
  241. meta_request->synced_data.num_parts_delivery_sent) {
  242. goto has_work_remaining;
  243. }
  244. }
  245. goto no_work_remaining;
  246. has_work_remaining:
  247. work_remaining = true;
  248. if (request != NULL) {
  249. AWS_LOGF_DEBUG(
  250. AWS_LS_S3_META_REQUEST,
  251. "id=%p: Returning request %p for part %d of %d",
  252. (void *)meta_request,
  253. (void *)request,
  254. request->part_number,
  255. auto_ranged_get->synced_data.total_num_parts);
  256. }
  257. no_work_remaining:
  258. if (!work_remaining) {
  259. aws_s3_meta_request_set_success_synced(meta_request, s_s3_auto_ranged_get_success_status(meta_request));
  260. if (auto_ranged_get->synced_data.num_parts_checksum_validated ==
  261. auto_ranged_get->synced_data.num_parts_requested) {
  262. /* If we have validated the checksum for every parts, we set the meta request level checksum validation
  263. * result.*/
  264. meta_request->synced_data.finish_result.did_validate = true;
  265. meta_request->synced_data.finish_result.validation_algorithm = auto_ranged_get->validation_algorithm;
  266. }
  267. }
  268. aws_s3_meta_request_unlock_synced_data(meta_request);
  269. }
  270. /* END CRITICAL SECTION */
  271. if (work_remaining) {
  272. *out_request = request;
  273. } else {
  274. AWS_ASSERT(request == NULL);
  275. aws_s3_meta_request_finish(meta_request);
  276. }
  277. return work_remaining;
  278. }
  279. /* Given a request, prepare it for sending based on its description. */
  280. static int s_s3_auto_ranged_get_prepare_request(
  281. struct aws_s3_meta_request *meta_request,
  282. struct aws_s3_request *request) {
  283. AWS_PRECONDITION(meta_request);
  284. AWS_PRECONDITION(request);
  285. /* Generate a new ranged get request based on the original message. */
  286. struct aws_http_message *message = NULL;
  287. struct aws_s3_auto_ranged_get *auto_ranged_get = meta_request->impl;
  288. switch (request->request_tag) {
  289. case AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_HEAD_OBJECT:
  290. /* A head object will be a copy of the original headers but with a HEAD request method. */
  291. message = aws_s3_message_util_copy_http_message_no_body_all_headers(
  292. meta_request->allocator, meta_request->initial_request_message);
  293. if (message) {
  294. aws_http_message_set_request_method(message, g_head_method);
  295. }
  296. break;
  297. case AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_PART:
  298. message = aws_s3_ranged_get_object_message_new(
  299. meta_request->allocator,
  300. meta_request->initial_request_message,
  301. request->part_range_start,
  302. request->part_range_end);
  303. break;
  304. case AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_INITIAL_MESSAGE:
  305. message = aws_s3_message_util_copy_http_message_no_body_all_headers(
  306. meta_request->allocator, meta_request->initial_request_message);
  307. break;
  308. }
  309. if (message == NULL) {
  310. AWS_LOGF_ERROR(
  311. AWS_LS_S3_META_REQUEST,
  312. "id=%p Could not create message for request with tag %d for auto-ranged-get meta request.",
  313. (void *)meta_request,
  314. request->request_tag);
  315. goto message_alloc_failed;
  316. }
  317. if (meta_request->checksum_config.validate_response_checksum) {
  318. aws_http_headers_set(aws_http_message_get_headers(message), g_request_validation_mode, g_enabled);
  319. }
  320. if (!auto_ranged_get->initial_message_has_if_match_header && auto_ranged_get->etag) {
  321. /* Add the if_match to the request */
  322. AWS_LOGF_DEBUG(
  323. AWS_LS_S3_META_REQUEST,
  324. "id=%p: Added the If-Match header to request %p for part %d",
  325. (void *)meta_request,
  326. (void *)request,
  327. request->part_number);
  328. aws_http_headers_set(
  329. aws_http_message_get_headers(message),
  330. g_if_match_header_name,
  331. aws_byte_cursor_from_string(auto_ranged_get->etag));
  332. }
  333. aws_s3_request_setup_send_data(request, message);
  334. aws_http_message_release(message);
  335. AWS_LOGF_DEBUG(
  336. AWS_LS_S3_META_REQUEST,
  337. "id=%p: Created request %p for part %d",
  338. (void *)meta_request,
  339. (void *)request,
  340. request->part_number);
  341. return AWS_OP_SUCCESS;
  342. message_alloc_failed:
  343. return AWS_OP_ERR;
  344. }
  345. /* Check the finish result of meta request, in case of the request failed because of downloading an empty file */
  346. static bool s_check_empty_file_download_error(struct aws_s3_request *failed_request) {
  347. struct aws_http_headers *failed_headers = failed_request->send_data.response_headers;
  348. struct aws_byte_buf failed_body = failed_request->send_data.response_body;
  349. if (failed_headers && failed_body.capacity > 0) {
  350. struct aws_byte_cursor content_type;
  351. AWS_ZERO_STRUCT(content_type);
  352. if (!aws_http_headers_get(failed_headers, g_content_type_header_name, &content_type)) {
  353. /* Content type found */
  354. if (aws_byte_cursor_eq_ignore_case(&content_type, &g_application_xml_value)) {
  355. /* XML response */
  356. struct aws_byte_cursor body_cursor = aws_byte_cursor_from_buf(&failed_body);
  357. struct aws_string *size =
  358. aws_xml_get_top_level_tag(failed_request->allocator, &g_object_size_value, &body_cursor);
  359. bool check_size = aws_string_eq_c_str(size, "0");
  360. aws_string_destroy(size);
  361. if (check_size) {
  362. return true;
  363. }
  364. }
  365. }
  366. }
  367. return false;
  368. }
  369. static int s_discover_object_range_and_content_length(
  370. struct aws_s3_meta_request *meta_request,
  371. struct aws_s3_request *request,
  372. int error_code,
  373. uint64_t *out_total_content_length,
  374. uint64_t *out_object_range_start,
  375. uint64_t *out_object_range_end) {
  376. AWS_PRECONDITION(out_total_content_length);
  377. AWS_PRECONDITION(out_object_range_start);
  378. AWS_PRECONDITION(out_object_range_end);
  379. int result = AWS_OP_ERR;
  380. uint64_t total_content_length = 0;
  381. uint64_t object_range_start = 0;
  382. uint64_t object_range_end = 0;
  383. AWS_ASSERT(request->discovers_object_size);
  384. struct aws_s3_auto_ranged_get *auto_ranged_get = meta_request->impl;
  385. switch (request->request_tag) {
  386. case AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_HEAD_OBJECT:
  387. if (error_code != AWS_ERROR_SUCCESS) {
  388. /* If the head request failed, there's nothing we can do, so resurface the error code. */
  389. aws_raise_error(error_code);
  390. break;
  391. }
  392. /* There should be a Content-Length header that indicates the total size of the range.*/
  393. if (aws_s3_parse_content_length_response_header(
  394. meta_request->allocator, request->send_data.response_headers, &total_content_length)) {
  395. AWS_LOGF_ERROR(
  396. AWS_LS_S3_META_REQUEST,
  397. "id=%p Could not find content-length header for request %p",
  398. (void *)meta_request,
  399. (void *)request);
  400. break;
  401. }
  402. /* if the inital message had a ranged header, there should also be a Content-Range header that specifies the
  403. * object range and total object size. Otherwise the size and range should be equal to the
  404. * total_content_length. */
  405. if (!auto_ranged_get->initial_message_has_range_header) {
  406. object_range_end = total_content_length - 1;
  407. } else if (aws_s3_parse_content_range_response_header(
  408. meta_request->allocator,
  409. request->send_data.response_headers,
  410. &object_range_start,
  411. &object_range_end,
  412. NULL)) {
  413. AWS_LOGF_ERROR(
  414. AWS_LS_S3_META_REQUEST,
  415. "id=%p Could not find content-range header for request %p",
  416. (void *)meta_request,
  417. (void *)request);
  418. break;
  419. }
  420. result = AWS_OP_SUCCESS;
  421. break;
  422. case AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_PART:
  423. AWS_ASSERT(request->part_number == 1);
  424. if (error_code != AWS_ERROR_SUCCESS) {
  425. /* If we hit an empty file while trying to discover the object-size via part, then this request
  426. failure
  427. * is as designed. */
  428. if (s_check_empty_file_download_error(request)) {
  429. AWS_LOGF_DEBUG(
  430. AWS_LS_S3_META_REQUEST,
  431. "id=%p Detected empty file with request %p. Sending new request without range header.",
  432. (void *)meta_request,
  433. (void *)request);
  434. total_content_length = 0ULL;
  435. result = AWS_OP_SUCCESS;
  436. } else {
  437. /* Otherwise, resurface the error code. */
  438. aws_raise_error(error_code);
  439. }
  440. break;
  441. }
  442. AWS_ASSERT(request->send_data.response_headers != NULL);
  443. /* Parse the object size from the part response. */
  444. if (aws_s3_parse_content_range_response_header(
  445. meta_request->allocator, request->send_data.response_headers, NULL, NULL, &total_content_length)) {
  446. AWS_LOGF_ERROR(
  447. AWS_LS_S3_META_REQUEST,
  448. "id=%p Could not find content-range header for request %p",
  449. (void *)meta_request,
  450. (void *)request);
  451. break;
  452. }
  453. /* When discovering the object size via first-part, the object range is the entire object. */
  454. object_range_start = 0;
  455. object_range_end = total_content_length - 1;
  456. result = AWS_OP_SUCCESS;
  457. break;
  458. default:
  459. AWS_ASSERT(false);
  460. break;
  461. }
  462. if (result == AWS_OP_SUCCESS) {
  463. *out_total_content_length = total_content_length;
  464. *out_object_range_start = object_range_start;
  465. *out_object_range_end = object_range_end;
  466. }
  467. return result;
  468. }
  469. static void s_s3_auto_ranged_get_request_finished(
  470. struct aws_s3_meta_request *meta_request,
  471. struct aws_s3_request *request,
  472. int error_code) {
  473. AWS_PRECONDITION(meta_request);
  474. AWS_PRECONDITION(meta_request->impl);
  475. AWS_PRECONDITION(request);
  476. struct aws_s3_auto_ranged_get *auto_ranged_get = meta_request->impl;
  477. AWS_PRECONDITION(auto_ranged_get);
  478. uint64_t total_content_length = 0ULL;
  479. uint64_t object_range_start = 0ULL;
  480. uint64_t object_range_end = 0ULL;
  481. bool found_object_size = false;
  482. bool request_failed = error_code != AWS_ERROR_SUCCESS;
  483. if (request->discovers_object_size) {
  484. /* Try to discover the object-range and content length.*/
  485. if (s_discover_object_range_and_content_length(
  486. meta_request, request, error_code, &total_content_length, &object_range_start, &object_range_end)) {
  487. error_code = aws_last_error_or_unknown();
  488. goto update_synced_data;
  489. }
  490. if (!request_failed && !auto_ranged_get->initial_message_has_if_match_header) {
  491. AWS_ASSERT(auto_ranged_get->etag == NULL);
  492. struct aws_byte_cursor etag_header_value;
  493. if (aws_http_headers_get(request->send_data.response_headers, g_etag_header_name, &etag_header_value)) {
  494. aws_raise_error(AWS_ERROR_S3_MISSING_ETAG);
  495. error_code = AWS_ERROR_S3_MISSING_ETAG;
  496. goto update_synced_data;
  497. }
  498. AWS_LOGF_TRACE(
  499. AWS_LS_S3_META_REQUEST,
  500. "id=%p Etag received for the meta request. value is: " PRInSTR "",
  501. (void *)meta_request,
  502. AWS_BYTE_CURSOR_PRI(etag_header_value));
  503. auto_ranged_get->etag = aws_string_new_from_cursor(auto_ranged_get->base.allocator, &etag_header_value);
  504. }
  505. /* If we were able to discover the object-range/content length successfully, then any error code that was passed
  506. * into this function is being handled and does not indicate an overall failure.*/
  507. error_code = AWS_ERROR_SUCCESS;
  508. found_object_size = true;
  509. if (meta_request->headers_callback != NULL) {
  510. struct aws_http_headers *response_headers = aws_http_headers_new(meta_request->allocator);
  511. copy_http_headers(request->send_data.response_headers, response_headers);
  512. /* If this request is a part, then the content range isn't applicable. */
  513. if (request->request_tag == AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_PART) {
  514. /* For now, we can assume that discovery of size via the first part of the object does not apply to
  515. * breaking up a ranged request. If it ever does, then we will need to repopulate this header. */
  516. AWS_ASSERT(!auto_ranged_get->initial_message_has_range_header);
  517. aws_http_headers_erase(response_headers, g_content_range_header_name);
  518. }
  519. char content_length_buffer[64] = "";
  520. snprintf(content_length_buffer, sizeof(content_length_buffer), "%" PRIu64, total_content_length);
  521. aws_http_headers_set(
  522. response_headers, g_content_length_header_name, aws_byte_cursor_from_c_str(content_length_buffer));
  523. if (meta_request->headers_callback(
  524. meta_request,
  525. response_headers,
  526. s_s3_auto_ranged_get_success_status(meta_request),
  527. meta_request->user_data)) {
  528. error_code = aws_last_error_or_unknown();
  529. }
  530. meta_request->headers_callback = NULL;
  531. aws_http_headers_release(response_headers);
  532. }
  533. }
  534. update_synced_data:
  535. /* BEGIN CRITICAL SECTION */
  536. {
  537. aws_s3_meta_request_lock_synced_data(meta_request);
  538. /* If the object range was found, then record it. */
  539. if (found_object_size) {
  540. AWS_ASSERT(!auto_ranged_get->synced_data.object_range_known);
  541. auto_ranged_get->synced_data.object_range_known = true;
  542. auto_ranged_get->synced_data.object_range_start = object_range_start;
  543. auto_ranged_get->synced_data.object_range_end = object_range_end;
  544. auto_ranged_get->synced_data.total_num_parts =
  545. aws_s3_get_num_parts(meta_request->part_size, object_range_start, object_range_end);
  546. }
  547. switch (request->request_tag) {
  548. case AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_HEAD_OBJECT:
  549. auto_ranged_get->synced_data.head_object_completed = true;
  550. AWS_LOGF_DEBUG(AWS_LS_S3_META_REQUEST, "id=%p Head object completed.", (void *)meta_request);
  551. break;
  552. case AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_PART:
  553. ++auto_ranged_get->synced_data.num_parts_completed;
  554. if (!request_failed) {
  555. /* Record the number of parts that checksum has been validated */
  556. if (request->did_validate) {
  557. if (auto_ranged_get->validation_algorithm == AWS_SCA_NONE) {
  558. auto_ranged_get->validation_algorithm = request->validation_algorithm;
  559. }
  560. /* They should be the same. */
  561. AWS_ASSERT(auto_ranged_get->validation_algorithm == request->validation_algorithm);
  562. ++auto_ranged_get->synced_data.num_parts_checksum_validated;
  563. }
  564. ++auto_ranged_get->synced_data.num_parts_successful;
  565. aws_s3_meta_request_stream_response_body_synced(meta_request, request);
  566. AWS_LOGF_DEBUG(
  567. AWS_LS_S3_META_REQUEST,
  568. "id=%p: %d out of %d parts have completed.",
  569. (void *)meta_request,
  570. (auto_ranged_get->synced_data.num_parts_successful +
  571. auto_ranged_get->synced_data.num_parts_failed),
  572. auto_ranged_get->synced_data.total_num_parts);
  573. } else {
  574. ++auto_ranged_get->synced_data.num_parts_failed;
  575. }
  576. break;
  577. case AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_INITIAL_MESSAGE:
  578. AWS_LOGF_DEBUG(
  579. AWS_LS_S3_META_REQUEST, "id=%p Get of file using initial message completed.", (void *)meta_request);
  580. auto_ranged_get->synced_data.get_without_range_completed = true;
  581. break;
  582. }
  583. if (error_code != AWS_ERROR_SUCCESS) {
  584. if (error_code == AWS_ERROR_S3_INVALID_RESPONSE_STATUS &&
  585. request->send_data.response_status == AWS_HTTP_STATUS_CODE_412_PRECONDITION_FAILED &&
  586. !auto_ranged_get->initial_message_has_if_match_header) {
  587. /* Use more clear error code as we added the if-match header under the hood. */
  588. error_code = AWS_ERROR_S3_OBJECT_MODIFIED;
  589. }
  590. aws_s3_meta_request_set_fail_synced(meta_request, request, error_code);
  591. if (error_code == AWS_ERROR_S3_RESPONSE_CHECKSUM_MISMATCH) {
  592. /* It's a mismatch of checksum, tell user that we validated the checksum and the algorithm we validated
  593. */
  594. meta_request->synced_data.finish_result.did_validate = true;
  595. meta_request->synced_data.finish_result.validation_algorithm = request->validation_algorithm;
  596. }
  597. }
  598. aws_s3_meta_request_unlock_synced_data(meta_request);
  599. }
  600. /* END CRITICAL SECTION */
  601. }