s3_copy_object.c 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787
  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_copy_object.h"
  6. #include "aws/s3/private/s3_request_messages.h"
  7. #include "aws/s3/private/s3_util.h"
  8. #include <aws/common/string.h>
  9. #include <aws/io/stream.h>
  10. /* Objects with size smaller than the constant below are bypassed as S3 CopyObject instead of multipart copy */
  11. static const size_t s_multipart_copy_minimum_object_size = 1L * 1024L * 1024L * 1024L;
  12. static const size_t s_etags_initial_capacity = 16;
  13. static const struct aws_byte_cursor s_upload_id = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("UploadId");
  14. static const size_t s_complete_multipart_upload_init_body_size_bytes = 512;
  15. static const size_t s_abort_multipart_upload_init_body_size_bytes = 512;
  16. static const struct aws_byte_cursor s_create_multipart_upload_copy_headers[] = {
  17. AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-server-side-encryption-customer-algorithm"),
  18. AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-server-side-encryption-customer-key-MD5"),
  19. AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-server-side-encryption-context"),
  20. };
  21. static void s_s3_meta_request_copy_object_destroy(struct aws_s3_meta_request *meta_request);
  22. static bool s_s3_copy_object_update(
  23. struct aws_s3_meta_request *meta_request,
  24. uint32_t flags,
  25. struct aws_s3_request **out_request);
  26. static int s_s3_copy_object_prepare_request(struct aws_s3_meta_request *meta_request, struct aws_s3_request *request);
  27. static void s_s3_copy_object_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_copy_object_vtable = {
  32. .update = s_s3_copy_object_update,
  33. .send_request_finish = aws_s3_meta_request_send_request_finish_handle_async_error,
  34. .prepare_request = s_s3_copy_object_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_copy_object_request_finished,
  38. .destroy = s_s3_meta_request_copy_object_destroy,
  39. .finish = aws_s3_meta_request_finish_default,
  40. };
  41. /* Allocate a new copy object meta request */
  42. struct aws_s3_meta_request *aws_s3_meta_request_copy_object_new(
  43. struct aws_allocator *allocator,
  44. struct aws_s3_client *client,
  45. const struct aws_s3_meta_request_options *options) {
  46. /* These should already have been validated by the caller. */
  47. AWS_PRECONDITION(allocator);
  48. AWS_PRECONDITION(client);
  49. AWS_PRECONDITION(options);
  50. AWS_PRECONDITION(options->message);
  51. struct aws_s3_copy_object *copy_object = aws_mem_calloc(allocator, 1, sizeof(struct aws_s3_copy_object));
  52. /* part size and content length will be fetched later using a HEAD object request */
  53. const size_t UNKNOWN_PART_SIZE = 0;
  54. const size_t UNKNOWN_CONTENT_LENGTH = 0;
  55. const int UNKNOWN_NUM_PARTS = 0;
  56. /* TODO Handle and test multipart copy */
  57. if (aws_s3_meta_request_init_base(
  58. allocator,
  59. client,
  60. UNKNOWN_PART_SIZE,
  61. false,
  62. options,
  63. copy_object,
  64. &s_s3_copy_object_vtable,
  65. &copy_object->base)) {
  66. aws_mem_release(allocator, copy_object);
  67. return NULL;
  68. }
  69. aws_array_list_init_dynamic(
  70. &copy_object->synced_data.etag_list, allocator, s_etags_initial_capacity, sizeof(struct aws_string *));
  71. copy_object->synced_data.content_length = UNKNOWN_CONTENT_LENGTH;
  72. copy_object->synced_data.total_num_parts = UNKNOWN_NUM_PARTS;
  73. copy_object->threaded_update_data.next_part_number = 1;
  74. AWS_LOGF_DEBUG(AWS_LS_S3_META_REQUEST, "id=%p Created new CopyObject Meta Request.", (void *)&copy_object->base);
  75. return &copy_object->base;
  76. }
  77. static void s_s3_meta_request_copy_object_destroy(struct aws_s3_meta_request *meta_request) {
  78. AWS_PRECONDITION(meta_request);
  79. AWS_PRECONDITION(meta_request->impl);
  80. struct aws_s3_copy_object *copy_object = meta_request->impl;
  81. aws_string_destroy(copy_object->upload_id);
  82. copy_object->upload_id = NULL;
  83. for (size_t etag_index = 0; etag_index < aws_array_list_length(&copy_object->synced_data.etag_list); ++etag_index) {
  84. struct aws_string *etag = NULL;
  85. aws_array_list_get_at(&copy_object->synced_data.etag_list, &etag, etag_index);
  86. aws_string_destroy(etag);
  87. }
  88. aws_array_list_clean_up(&copy_object->synced_data.etag_list);
  89. aws_http_headers_release(copy_object->synced_data.needed_response_headers);
  90. aws_mem_release(meta_request->allocator, copy_object);
  91. }
  92. static bool s_s3_copy_object_update(
  93. struct aws_s3_meta_request *meta_request,
  94. uint32_t flags,
  95. struct aws_s3_request **out_request) {
  96. AWS_PRECONDITION(meta_request);
  97. AWS_PRECONDITION(out_request);
  98. struct aws_s3_request *request = NULL;
  99. bool work_remaining = false;
  100. struct aws_s3_copy_object *copy_object = meta_request->impl;
  101. aws_s3_meta_request_lock_synced_data(meta_request);
  102. if (!aws_s3_meta_request_has_finish_result_synced(meta_request)) {
  103. /* If we haven't already sent the GetObject HEAD request to get the source object size, do so now. */
  104. if (!copy_object->synced_data.head_object_sent) {
  105. request = aws_s3_request_new(
  106. meta_request,
  107. AWS_S3_COPY_OBJECT_REQUEST_TAG_GET_OBJECT_SIZE,
  108. 0,
  109. AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS);
  110. copy_object->synced_data.head_object_sent = true;
  111. goto has_work_remaining;
  112. }
  113. if (!copy_object->synced_data.head_object_completed) {
  114. /* we have not received the object size response yet */
  115. goto has_work_remaining;
  116. }
  117. if (copy_object->synced_data.content_length < s_multipart_copy_minimum_object_size) {
  118. /* object is too small to use multipart upload: forwards the original CopyObject request to S3 instead. */
  119. if (!copy_object->synced_data.copy_request_bypass_sent) {
  120. request = aws_s3_request_new(
  121. meta_request,
  122. AWS_S3_COPY_OBJECT_REQUEST_TAG_BYPASS,
  123. 1,
  124. AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS);
  125. AWS_LOGF_DEBUG(
  126. AWS_LS_S3_META_REQUEST,
  127. "id=%p: Meta Request CopyObject created bypass request %p",
  128. (void *)meta_request,
  129. (void *)request);
  130. copy_object->synced_data.copy_request_bypass_sent = true;
  131. goto has_work_remaining;
  132. }
  133. /* If the bypass request hasn't been completed, then wait for it to be completed. */
  134. if (!copy_object->synced_data.copy_request_bypass_completed) {
  135. goto has_work_remaining;
  136. } else {
  137. goto no_work_remaining;
  138. }
  139. }
  140. /* Object size is large enough to use multipart copy. If we haven't already sent a create-multipart-upload
  141. * message, do so now. */
  142. if (!copy_object->synced_data.create_multipart_upload_sent) {
  143. request = aws_s3_request_new(
  144. meta_request,
  145. AWS_S3_COPY_OBJECT_REQUEST_TAG_CREATE_MULTIPART_UPLOAD,
  146. 0,
  147. AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS);
  148. copy_object->synced_data.create_multipart_upload_sent = true;
  149. goto has_work_remaining;
  150. }
  151. /* If the create-multipart-upload message hasn't been completed, then there is still additional work to do, but
  152. * it can't be done yet. */
  153. if (!copy_object->synced_data.create_multipart_upload_completed) {
  154. goto has_work_remaining;
  155. }
  156. /* If we haven't sent all of the parts yet, then set up to send a new part now. */
  157. if (copy_object->synced_data.num_parts_sent < copy_object->synced_data.total_num_parts) {
  158. if ((flags & AWS_S3_META_REQUEST_UPDATE_FLAG_CONSERVATIVE) != 0) {
  159. uint32_t num_parts_in_flight =
  160. (copy_object->synced_data.num_parts_sent - copy_object->synced_data.num_parts_completed);
  161. /* TODO: benchmark if there is need to limit the amount of upload part copy in flight requests */
  162. if (num_parts_in_flight > 0) {
  163. goto has_work_remaining;
  164. }
  165. }
  166. /* Allocate a request for another part. */
  167. request = aws_s3_request_new(
  168. meta_request,
  169. AWS_S3_COPY_OBJECT_REQUEST_TAG_MULTIPART_COPY,
  170. 0,
  171. AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS);
  172. request->part_number = copy_object->threaded_update_data.next_part_number;
  173. ++copy_object->threaded_update_data.next_part_number;
  174. ++copy_object->synced_data.num_parts_sent;
  175. AWS_LOGF_DEBUG(
  176. AWS_LS_S3_META_REQUEST,
  177. "id=%p: Returning request %p for part %d",
  178. (void *)meta_request,
  179. (void *)request,
  180. request->part_number);
  181. goto has_work_remaining;
  182. }
  183. /* There is one more request to send after all of the parts (the complete-multipart-upload) but it can't be done
  184. * until all of the parts have been completed.*/
  185. if (copy_object->synced_data.num_parts_completed != copy_object->synced_data.total_num_parts) {
  186. goto has_work_remaining;
  187. }
  188. /* If the complete-multipart-upload request hasn't been set yet, then send it now. */
  189. if (!copy_object->synced_data.complete_multipart_upload_sent) {
  190. request = aws_s3_request_new(
  191. meta_request,
  192. AWS_S3_COPY_OBJECT_REQUEST_TAG_COMPLETE_MULTIPART_UPLOAD,
  193. 0,
  194. AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS);
  195. copy_object->synced_data.complete_multipart_upload_sent = true;
  196. goto has_work_remaining;
  197. }
  198. /* Wait for the complete-multipart-upload request to finish. */
  199. if (!copy_object->synced_data.complete_multipart_upload_completed) {
  200. goto has_work_remaining;
  201. }
  202. goto no_work_remaining;
  203. } else {
  204. /* If the create multipart upload hasn't been sent, then there is nothing left to do when canceling. */
  205. if (!copy_object->synced_data.create_multipart_upload_sent) {
  206. goto no_work_remaining;
  207. }
  208. /* If the create-multipart-upload request is still in flight, wait for it to finish. */
  209. if (!copy_object->synced_data.create_multipart_upload_completed) {
  210. goto has_work_remaining;
  211. }
  212. /* If the number of parts completed is less than the number of parts sent, then we need to wait until all of
  213. * those parts are done sending before aborting. */
  214. if (copy_object->synced_data.num_parts_completed < copy_object->synced_data.num_parts_sent) {
  215. goto has_work_remaining;
  216. }
  217. /* If the complete-multipart-upload is already in flight, then we can't necessarily send an abort. */
  218. if (copy_object->synced_data.complete_multipart_upload_sent &&
  219. !copy_object->synced_data.complete_multipart_upload_completed) {
  220. goto has_work_remaining;
  221. }
  222. /* If the complete-multipart-upload completed successfully, then there is nothing to abort since the transfer
  223. * has already finished. */
  224. if (copy_object->synced_data.complete_multipart_upload_completed &&
  225. copy_object->synced_data.complete_multipart_upload_error_code == AWS_ERROR_SUCCESS) {
  226. goto no_work_remaining;
  227. }
  228. /* If we made it here, and the abort-multipart-upload message hasn't been sent yet, then do so now. */
  229. if (!copy_object->synced_data.abort_multipart_upload_sent) {
  230. if (copy_object->upload_id == NULL) {
  231. goto no_work_remaining;
  232. }
  233. request = aws_s3_request_new(
  234. meta_request,
  235. AWS_S3_COPY_OBJECT_REQUEST_TAG_ABORT_MULTIPART_UPLOAD,
  236. 0,
  237. AWS_S3_REQUEST_FLAG_RECORD_RESPONSE_HEADERS | AWS_S3_REQUEST_FLAG_ALWAYS_SEND);
  238. copy_object->synced_data.abort_multipart_upload_sent = true;
  239. goto has_work_remaining;
  240. }
  241. /* Wait for the multipart upload to be completed. */
  242. if (!copy_object->synced_data.abort_multipart_upload_completed) {
  243. goto has_work_remaining;
  244. }
  245. goto no_work_remaining;
  246. }
  247. has_work_remaining:
  248. work_remaining = true;
  249. no_work_remaining:
  250. if (!work_remaining) {
  251. aws_s3_meta_request_set_success_synced(meta_request, AWS_S3_RESPONSE_STATUS_SUCCESS);
  252. }
  253. aws_s3_meta_request_unlock_synced_data(meta_request);
  254. if (work_remaining) {
  255. *out_request = request;
  256. } else {
  257. AWS_ASSERT(request == NULL);
  258. aws_s3_meta_request_finish(meta_request);
  259. }
  260. return work_remaining;
  261. }
  262. /* Given a request, prepare it for sending based on its description. */
  263. static int s_s3_copy_object_prepare_request(struct aws_s3_meta_request *meta_request, struct aws_s3_request *request) {
  264. AWS_PRECONDITION(meta_request);
  265. struct aws_s3_copy_object *copy_object = meta_request->impl;
  266. AWS_PRECONDITION(copy_object);
  267. aws_s3_meta_request_lock_synced_data(meta_request);
  268. struct aws_http_message *message = NULL;
  269. switch (request->request_tag) {
  270. /* Prepares the GetObject HEAD request to get the source object size. */
  271. case AWS_S3_COPY_OBJECT_REQUEST_TAG_GET_OBJECT_SIZE: {
  272. message = aws_s3_get_source_object_size_message_new(
  273. meta_request->allocator, meta_request->initial_request_message);
  274. break;
  275. }
  276. /* The S3 object is not large enough for multi-part copy. Bypasses a copy of the original CopyObject request to
  277. * S3. */
  278. case AWS_S3_COPY_OBJECT_REQUEST_TAG_BYPASS: {
  279. message = aws_s3_message_util_copy_http_message_no_body_all_headers(
  280. meta_request->allocator, meta_request->initial_request_message);
  281. break;
  282. }
  283. /* Prepares the CreateMultipartUpload sub-request. */
  284. case AWS_S3_COPY_OBJECT_REQUEST_TAG_CREATE_MULTIPART_UPLOAD: {
  285. uint64_t part_size_uint64 = copy_object->synced_data.content_length / (uint64_t)g_s3_max_num_upload_parts;
  286. if (part_size_uint64 > SIZE_MAX) {
  287. AWS_LOGF_ERROR(
  288. AWS_LS_S3_META_REQUEST,
  289. "Could not create multipart copy meta request; required part size of %" PRIu64
  290. " bytes is too large for platform.",
  291. part_size_uint64);
  292. aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
  293. return AWS_OP_ERR;
  294. }
  295. size_t part_size = (size_t)part_size_uint64;
  296. const size_t MIN_PART_SIZE = 64L * 1024L * 1024L; /* minimum partition size */
  297. if (part_size < MIN_PART_SIZE) {
  298. part_size = MIN_PART_SIZE;
  299. }
  300. uint32_t num_parts = (uint32_t)(copy_object->synced_data.content_length / part_size);
  301. if ((copy_object->synced_data.content_length % part_size) > 0) {
  302. ++num_parts;
  303. }
  304. copy_object->synced_data.total_num_parts = num_parts;
  305. copy_object->synced_data.part_size = part_size;
  306. AWS_LOGF_DEBUG(
  307. AWS_LS_S3_META_REQUEST,
  308. "Starting multi-part Copy using part size=%zu, total_num_parts=%zu",
  309. part_size,
  310. (size_t)num_parts);
  311. /* Create the message to create a new multipart upload. */
  312. message = aws_s3_create_multipart_upload_message_new(
  313. meta_request->allocator,
  314. meta_request->initial_request_message,
  315. meta_request->checksum_config.checksum_algorithm);
  316. break;
  317. }
  318. /* Prepares the UploadPartCopy sub-request. */
  319. case AWS_S3_COPY_OBJECT_REQUEST_TAG_MULTIPART_COPY: {
  320. /* Create a new uploadPartCopy message to upload a part. */
  321. /* compute sub-request range */
  322. uint64_t range_start = (request->part_number - 1) * copy_object->synced_data.part_size;
  323. uint64_t range_end = range_start + copy_object->synced_data.part_size - 1;
  324. if (range_end >= copy_object->synced_data.content_length) {
  325. /* adjust size of last part */
  326. range_end = copy_object->synced_data.content_length - 1;
  327. }
  328. AWS_LOGF_DEBUG(
  329. AWS_LS_S3_META_REQUEST,
  330. "Starting UploadPartCopy for partition %" PRIu32 ", range_start=%" PRIu64 ", range_end=%" PRIu64
  331. ", full object length=%" PRIu64,
  332. request->part_number,
  333. range_start,
  334. range_end,
  335. copy_object->synced_data.content_length);
  336. message = aws_s3_upload_part_copy_message_new(
  337. meta_request->allocator,
  338. meta_request->initial_request_message,
  339. &request->request_body,
  340. request->part_number,
  341. range_start,
  342. range_end,
  343. copy_object->upload_id,
  344. meta_request->should_compute_content_md5);
  345. break;
  346. }
  347. /* Prepares the CompleteMultiPartUpload sub-request. */
  348. case AWS_S3_COPY_OBJECT_REQUEST_TAG_COMPLETE_MULTIPART_UPLOAD: {
  349. if (request->num_times_prepared == 0) {
  350. aws_byte_buf_init(
  351. &request->request_body, meta_request->allocator, s_complete_multipart_upload_init_body_size_bytes);
  352. } else {
  353. aws_byte_buf_reset(&request->request_body, false);
  354. }
  355. AWS_FATAL_ASSERT(copy_object->upload_id);
  356. AWS_ASSERT(request->request_body.capacity > 0);
  357. aws_byte_buf_reset(&request->request_body, false);
  358. /* Build the message to complete our multipart upload, which includes a payload describing all of our
  359. * completed parts. */
  360. message = aws_s3_complete_multipart_message_new(
  361. meta_request->allocator,
  362. meta_request->initial_request_message,
  363. &request->request_body,
  364. copy_object->upload_id,
  365. &copy_object->synced_data.etag_list,
  366. NULL,
  367. AWS_SCA_NONE);
  368. break;
  369. }
  370. /* Prepares the AbortMultiPartUpload sub-request. */
  371. case AWS_S3_COPY_OBJECT_REQUEST_TAG_ABORT_MULTIPART_UPLOAD: {
  372. AWS_FATAL_ASSERT(copy_object->upload_id);
  373. AWS_LOGF_DEBUG(
  374. AWS_LS_S3_META_REQUEST,
  375. "id=%p Abort multipart upload request for upload id %s.",
  376. (void *)meta_request,
  377. aws_string_c_str(copy_object->upload_id));
  378. if (request->num_times_prepared == 0) {
  379. aws_byte_buf_init(
  380. &request->request_body, meta_request->allocator, s_abort_multipart_upload_init_body_size_bytes);
  381. } else {
  382. aws_byte_buf_reset(&request->request_body, false);
  383. }
  384. /* Build the message to abort our multipart upload */
  385. message = aws_s3_abort_multipart_upload_message_new(
  386. meta_request->allocator, meta_request->initial_request_message, copy_object->upload_id);
  387. break;
  388. }
  389. }
  390. aws_s3_meta_request_unlock_synced_data(meta_request);
  391. if (message == NULL) {
  392. AWS_LOGF_ERROR(
  393. AWS_LS_S3_META_REQUEST,
  394. "id=%p Could not allocate message for request with tag %d for CopyObject meta request.",
  395. (void *)meta_request,
  396. request->request_tag);
  397. goto message_create_failed;
  398. }
  399. aws_s3_request_setup_send_data(request, message);
  400. aws_http_message_release(message);
  401. AWS_LOGF_DEBUG(
  402. AWS_LS_S3_META_REQUEST,
  403. "id=%p: Prepared request %p for part %d",
  404. (void *)meta_request,
  405. (void *)request,
  406. request->part_number);
  407. return AWS_OP_SUCCESS;
  408. message_create_failed:
  409. return AWS_OP_ERR;
  410. }
  411. /* For UploadPartCopy requests, etag is sent in the request body, within XML entity quotes */
  412. static struct aws_string *s_etag_new_from_upload_part_copy_response(
  413. struct aws_allocator *allocator,
  414. struct aws_byte_buf *response_body) {
  415. struct aws_string *etag = NULL;
  416. struct aws_byte_cursor response_body_cursor = aws_byte_cursor_from_buf(response_body);
  417. struct aws_string *etag_within_xml_quotes =
  418. aws_xml_get_top_level_tag(allocator, &g_etag_header_name, &response_body_cursor);
  419. struct aws_byte_buf etag_within_quotes_byte_buf;
  420. AWS_ZERO_STRUCT(etag_within_quotes_byte_buf);
  421. replace_quote_entities(allocator, etag_within_xml_quotes, &etag_within_quotes_byte_buf);
  422. /* Remove the quotes surrounding the etag. */
  423. struct aws_byte_cursor etag_within_quotes_byte_cursor = aws_byte_cursor_from_buf(&etag_within_quotes_byte_buf);
  424. if (etag_within_quotes_byte_cursor.len >= 2 && etag_within_quotes_byte_cursor.ptr[0] == '"' &&
  425. etag_within_quotes_byte_cursor.ptr[etag_within_quotes_byte_cursor.len - 1] == '"') {
  426. aws_byte_cursor_advance(&etag_within_quotes_byte_cursor, 1);
  427. --etag_within_quotes_byte_cursor.len;
  428. }
  429. etag = aws_string_new_from_cursor(allocator, &etag_within_quotes_byte_cursor);
  430. aws_byte_buf_clean_up(&etag_within_quotes_byte_buf);
  431. aws_string_destroy(etag_within_xml_quotes);
  432. return etag;
  433. }
  434. static void s_s3_copy_object_request_finished(
  435. struct aws_s3_meta_request *meta_request,
  436. struct aws_s3_request *request,
  437. int error_code) {
  438. AWS_PRECONDITION(meta_request);
  439. AWS_PRECONDITION(meta_request->impl);
  440. AWS_PRECONDITION(request);
  441. struct aws_s3_copy_object *copy_object = meta_request->impl;
  442. aws_s3_meta_request_lock_synced_data(meta_request);
  443. switch (request->request_tag) {
  444. case AWS_S3_COPY_OBJECT_REQUEST_TAG_GET_OBJECT_SIZE: {
  445. if (error_code == AWS_ERROR_SUCCESS) {
  446. struct aws_byte_cursor content_length_cursor;
  447. if (!aws_http_headers_get(
  448. request->send_data.response_headers, g_content_length_header_name, &content_length_cursor)) {
  449. if (!aws_byte_cursor_utf8_parse_u64(
  450. content_length_cursor, &copy_object->synced_data.content_length)) {
  451. copy_object->synced_data.head_object_completed = true;
  452. } else {
  453. /* HEAD request returned an invalid content-length */
  454. aws_s3_meta_request_set_fail_synced(
  455. meta_request, request, AWS_ERROR_S3_INVALID_CONTENT_LENGTH_HEADER);
  456. }
  457. } else {
  458. /* HEAD request didn't return content-length header */
  459. aws_s3_meta_request_set_fail_synced(
  460. meta_request, request, AWS_ERROR_S3_INVALID_CONTENT_LENGTH_HEADER);
  461. }
  462. } else {
  463. aws_s3_meta_request_set_fail_synced(meta_request, request, error_code);
  464. }
  465. break;
  466. }
  467. /* The S3 object is not large enough for multi-part copy. A copy of the original CopyObject request
  468. * was bypassed to S3 and is now finished. */
  469. case AWS_S3_COPY_OBJECT_REQUEST_TAG_BYPASS: {
  470. /* Invoke headers callback if it was requested for this meta request */
  471. if (meta_request->headers_callback != NULL) {
  472. struct aws_http_headers *final_response_headers = aws_http_headers_new(meta_request->allocator);
  473. /* Copy all the response headers from this request. */
  474. copy_http_headers(request->send_data.response_headers, final_response_headers);
  475. /* Notify the user of the headers. */
  476. if (meta_request->headers_callback(
  477. meta_request,
  478. final_response_headers,
  479. request->send_data.response_status,
  480. meta_request->user_data)) {
  481. error_code = aws_last_error_or_unknown();
  482. }
  483. meta_request->headers_callback = NULL;
  484. aws_http_headers_release(final_response_headers);
  485. }
  486. /* Signals completion of the meta request */
  487. if (error_code == AWS_ERROR_SUCCESS) {
  488. copy_object->synced_data.copy_request_bypass_completed = true;
  489. } else {
  490. /* Bypassed CopyObject request failed */
  491. aws_s3_meta_request_set_fail_synced(meta_request, request, error_code);
  492. }
  493. break;
  494. }
  495. case AWS_S3_COPY_OBJECT_REQUEST_TAG_CREATE_MULTIPART_UPLOAD: {
  496. struct aws_http_headers *needed_response_headers = NULL;
  497. if (error_code == AWS_ERROR_SUCCESS) {
  498. needed_response_headers = aws_http_headers_new(meta_request->allocator);
  499. const size_t copy_header_count = AWS_ARRAY_SIZE(s_create_multipart_upload_copy_headers);
  500. /* Copy any headers now that we'll need for the final, transformed headers later. */
  501. for (size_t header_index = 0; header_index < copy_header_count; ++header_index) {
  502. const struct aws_byte_cursor *header_name = &s_create_multipart_upload_copy_headers[header_index];
  503. struct aws_byte_cursor header_value;
  504. AWS_ZERO_STRUCT(header_value);
  505. if (!aws_http_headers_get(request->send_data.response_headers, *header_name, &header_value)) {
  506. aws_http_headers_set(needed_response_headers, *header_name, header_value);
  507. }
  508. }
  509. struct aws_byte_cursor buffer_byte_cursor = aws_byte_cursor_from_buf(&request->send_data.response_body);
  510. /* Find the upload id for this multipart upload. */
  511. struct aws_string *upload_id =
  512. aws_xml_get_top_level_tag(meta_request->allocator, &s_upload_id, &buffer_byte_cursor);
  513. if (upload_id == NULL) {
  514. AWS_LOGF_ERROR(
  515. AWS_LS_S3_META_REQUEST,
  516. "id=%p Could not find upload-id in create-multipart-upload response",
  517. (void *)meta_request);
  518. aws_raise_error(AWS_ERROR_S3_MISSING_UPLOAD_ID);
  519. error_code = AWS_ERROR_S3_MISSING_UPLOAD_ID;
  520. } else {
  521. /* Store the multipart upload id. */
  522. copy_object->upload_id = upload_id;
  523. }
  524. }
  525. AWS_ASSERT(copy_object->synced_data.needed_response_headers == NULL);
  526. copy_object->synced_data.needed_response_headers = needed_response_headers;
  527. copy_object->synced_data.create_multipart_upload_completed = true;
  528. copy_object->synced_data.create_multipart_upload_error_code = error_code;
  529. if (error_code != AWS_ERROR_SUCCESS) {
  530. aws_s3_meta_request_set_fail_synced(meta_request, request, error_code);
  531. }
  532. break;
  533. }
  534. case AWS_S3_COPY_OBJECT_REQUEST_TAG_MULTIPART_COPY: {
  535. size_t part_number = request->part_number;
  536. AWS_FATAL_ASSERT(part_number > 0);
  537. size_t part_index = part_number - 1;
  538. ++copy_object->synced_data.num_parts_completed;
  539. AWS_LOGF_DEBUG(
  540. AWS_LS_S3_META_REQUEST,
  541. "id=%p: %d out of %d parts have completed.",
  542. (void *)meta_request,
  543. copy_object->synced_data.num_parts_completed,
  544. copy_object->synced_data.total_num_parts);
  545. if (error_code == AWS_ERROR_SUCCESS) {
  546. struct aws_string *etag = s_etag_new_from_upload_part_copy_response(
  547. meta_request->allocator, &request->send_data.response_body);
  548. AWS_ASSERT(etag != NULL);
  549. ++copy_object->synced_data.num_parts_successful;
  550. if (meta_request->progress_callback != NULL) {
  551. struct aws_s3_meta_request_progress progress = {
  552. .bytes_transferred = copy_object->synced_data.part_size,
  553. .content_length = copy_object->synced_data.content_length};
  554. meta_request->progress_callback(meta_request, &progress, meta_request->user_data);
  555. }
  556. struct aws_string *null_etag = NULL;
  557. /* ETags need to be associated with their part number, so we keep the etag indices consistent with
  558. * part numbers. This means we may have to add padding to the list in the case that parts finish out
  559. * of order. */
  560. while (aws_array_list_length(&copy_object->synced_data.etag_list) < part_number) {
  561. int push_back_result = aws_array_list_push_back(&copy_object->synced_data.etag_list, &null_etag);
  562. AWS_FATAL_ASSERT(push_back_result == AWS_OP_SUCCESS);
  563. }
  564. aws_array_list_set_at(&copy_object->synced_data.etag_list, &etag, part_index);
  565. } else {
  566. ++copy_object->synced_data.num_parts_failed;
  567. aws_s3_meta_request_set_fail_synced(meta_request, request, error_code);
  568. }
  569. break;
  570. }
  571. case AWS_S3_COPY_OBJECT_REQUEST_TAG_COMPLETE_MULTIPART_UPLOAD: {
  572. if (error_code == AWS_ERROR_SUCCESS && meta_request->headers_callback != NULL) {
  573. struct aws_http_headers *final_response_headers = aws_http_headers_new(meta_request->allocator);
  574. /* Copy all the response headers from this request. */
  575. copy_http_headers(request->send_data.response_headers, final_response_headers);
  576. /* Copy over any response headers that we've previously determined are needed for this final
  577. * response.
  578. */
  579. copy_http_headers(copy_object->synced_data.needed_response_headers, final_response_headers);
  580. struct aws_byte_cursor response_body_cursor =
  581. aws_byte_cursor_from_buf(&request->send_data.response_body);
  582. /* Grab the ETag for the entire object, and set it as a header. */
  583. struct aws_string *etag_header_value =
  584. aws_xml_get_top_level_tag(meta_request->allocator, &g_etag_header_name, &response_body_cursor);
  585. if (etag_header_value != NULL) {
  586. struct aws_byte_buf etag_header_value_byte_buf;
  587. AWS_ZERO_STRUCT(etag_header_value_byte_buf);
  588. replace_quote_entities(meta_request->allocator, etag_header_value, &etag_header_value_byte_buf);
  589. aws_http_headers_set(
  590. final_response_headers,
  591. g_etag_header_name,
  592. aws_byte_cursor_from_buf(&etag_header_value_byte_buf));
  593. aws_string_destroy(etag_header_value);
  594. aws_byte_buf_clean_up(&etag_header_value_byte_buf);
  595. }
  596. /* Notify the user of the headers. */
  597. if (meta_request->headers_callback(
  598. meta_request,
  599. final_response_headers,
  600. request->send_data.response_status,
  601. meta_request->user_data)) {
  602. error_code = aws_last_error_or_unknown();
  603. }
  604. meta_request->headers_callback = NULL;
  605. aws_http_headers_release(final_response_headers);
  606. }
  607. copy_object->synced_data.complete_multipart_upload_completed = true;
  608. copy_object->synced_data.complete_multipart_upload_error_code = error_code;
  609. if (error_code != AWS_ERROR_SUCCESS) {
  610. aws_s3_meta_request_set_fail_synced(meta_request, request, error_code);
  611. }
  612. break;
  613. }
  614. case AWS_S3_COPY_OBJECT_REQUEST_TAG_ABORT_MULTIPART_UPLOAD: {
  615. copy_object->synced_data.abort_multipart_upload_error_code = error_code;
  616. copy_object->synced_data.abort_multipart_upload_completed = true;
  617. break;
  618. }
  619. }
  620. aws_s3_meta_request_unlock_synced_data(meta_request);
  621. }