1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
|
#ifndef AWS_S3_META_REQUEST_IMPL_H
#define AWS_S3_META_REQUEST_IMPL_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/auth/signing.h>
#include <aws/common/atomics.h>
#include <aws/common/linked_list.h>
#include <aws/common/mutex.h>
#include <aws/common/ref_count.h>
#include <aws/common/task_scheduler.h>
#include <aws/http/request_response.h>
#include "aws/s3/private/s3_checksums.h"
#include "aws/s3/private/s3_client_impl.h"
#include "aws/s3/private/s3_request.h"
struct aws_s3_client;
struct aws_s3_connection;
struct aws_s3_meta_request;
struct aws_s3_request;
struct aws_http_headers;
struct aws_http_make_request_options;
struct aws_retry_strategy;
enum aws_s3_meta_request_state {
AWS_S3_META_REQUEST_STATE_ACTIVE,
AWS_S3_META_REQUEST_STATE_FINISHED,
};
enum aws_s3_meta_request_update_flags {
/* The client potentially has multiple meta requests that it can spread across connections, and the given meta
request can selectively not return a request if there is a performance reason to do so.*/
AWS_S3_META_REQUEST_UPDATE_FLAG_CONSERVATIVE = 0x00000002,
};
typedef void(aws_s3_meta_request_prepare_request_callback_fn)(
struct aws_s3_meta_request *meta_request,
struct aws_s3_request *request,
int error_code,
void *user_data);
struct aws_s3_prepare_request_payload {
struct aws_allocator *allocator;
struct aws_s3_request *request;
struct aws_task task;
/* async step: wait for vtable->prepare_request() call to complete */
struct aws_future_void *asyncstep_prepare_request;
/* callback to invoke when all request preparation work is complete */
aws_s3_meta_request_prepare_request_callback_fn *callback;
void *user_data;
};
/* An event to be delivered on the meta-request's io_event_loop thread. */
struct aws_s3_meta_request_event {
enum aws_s3_meta_request_event_type {
AWS_S3_META_REQUEST_EVENT_RESPONSE_BODY, /* body_callback */
AWS_S3_META_REQUEST_EVENT_PROGRESS, /* progress_callback */
AWS_S3_META_REQUEST_EVENT_TELEMETRY, /* telemetry_callback */
} type;
union {
/* data for AWS_S3_META_REQUEST_EVENT_RESPONSE_BODY */
struct {
struct aws_s3_request *completed_request;
} response_body;
/* data for AWS_S3_META_REQUEST_EVENT_PROGRESS */
struct {
struct aws_s3_meta_request_progress info;
} progress;
/* data for AWS_S3_META_REQUEST_EVENT_TELEMETRY */
struct {
struct aws_s3_request_metrics *metrics;
} telemetry;
} u;
};
struct aws_s3_meta_request_vtable {
/* Update the meta request. out_request is required to be non-null. Returns true if there is any work in
* progress, false if there is not. */
bool (*update)(struct aws_s3_meta_request *meta_request, uint32_t flags, struct aws_s3_request **out_request);
/* Run vtable->prepare_request() on the meta-request's event loop.
* We do this because body streaming is slow, and we don't want it on our networking threads.
* The callback may fire on any thread (an async sub-step may run on another thread). */
void (*schedule_prepare_request)(
struct aws_s3_meta_request *meta_request,
struct aws_s3_request *request,
aws_s3_meta_request_prepare_request_callback_fn *callback,
void *user_data);
/* Given a request, asynchronously prepare it for sending
* (creating the correct HTTP message, reading from a stream (if necessary), computing hashes, etc.).
* Returns a future, which may complete on any thread (and may complete synchronously). */
struct aws_future_void *(*prepare_request)(struct aws_s3_request *request);
void (*init_signing_date_time)(struct aws_s3_meta_request *meta_request, struct aws_date_time *date_time);
/* Sign the given request. */
void (*sign_request)(
struct aws_s3_meta_request *meta_request,
struct aws_s3_request *request,
aws_signing_complete_fn *on_signing_complete,
void *user_data);
/* Called when any sending of the request is finished, including for each retry. */
void (*send_request_finish)(struct aws_s3_connection *connection, struct aws_http_stream *stream, int error_code);
/* Called when the request is done being sent, and will not be retried/sent again. */
void (*finished_request)(struct aws_s3_meta_request *meta_request, struct aws_s3_request *request, int error_code);
/* Called by the derived meta request when the meta request is completely finished. */
void (*finish)(struct aws_s3_meta_request *meta_request);
/* Handle de-allocation of the meta request. */
void (*destroy)(struct aws_s3_meta_request *);
/* Pause the given request */
int (*pause)(struct aws_s3_meta_request *meta_request, struct aws_s3_meta_request_resume_token **resume_token);
};
/**
* This represents one meta request, ie, one accelerated file transfer. One S3 meta request can represent multiple S3
* requests.
*/
struct aws_s3_meta_request {
struct aws_allocator *allocator;
struct aws_ref_count ref_count;
void *impl;
struct aws_s3_meta_request_vtable *vtable;
/* Initial HTTP Message that this meta request is based on. */
struct aws_http_message *initial_request_message;
/* The meta request's outgoing body comes from one of these:
* 1) request_body_async_stream: if set, then async stream 1 part at a time
* 2) request_body_parallel_stream: if set, then stream multiple parts in parallel
* 3) initial_request_message's body_stream: else synchronously stream parts */
struct aws_async_input_stream *request_body_async_stream;
struct aws_parallel_input_stream *request_body_parallel_stream;
/* Part size to use for uploads and downloads. Passed down by the creating client. */
const size_t part_size;
struct aws_cached_signing_config_aws *cached_signing_config;
/* Client that created this meta request which also processes this request. After the meta request is finished, this
* reference is removed.*/
struct aws_s3_client *client;
struct aws_s3_endpoint *endpoint;
/* Event loop to schedule IO work related on, ie, reading from streams, streaming parts back to the caller, etc...
* After the meta request is finished, this will be reset along with the client reference.*/
struct aws_event_loop *io_event_loop;
/* User data to be passed to each customer specified callback.*/
void *user_data;
/* Customer specified callbacks. */
aws_s3_meta_request_headers_callback_fn *headers_callback;
aws_s3_meta_request_receive_body_callback_fn *body_callback;
aws_s3_meta_request_finish_fn *finish_callback;
aws_s3_meta_request_shutdown_fn *shutdown_callback;
aws_s3_meta_request_progress_fn *progress_callback;
aws_s3_meta_request_telemetry_fn *telemetry_callback;
aws_s3_meta_request_upload_review_fn *upload_review_callback;
/* Customer specified callbacks to be called by our specialized callback to calculate the response checksum. */
aws_s3_meta_request_headers_callback_fn *headers_user_callback_after_checksum;
aws_s3_meta_request_receive_body_callback_fn *body_user_callback_after_checksum;
aws_s3_meta_request_finish_fn *finish_user_callback_after_checksum;
enum aws_s3_meta_request_type type;
struct aws_string *s3express_session_host;
struct {
struct aws_mutex lock;
/* Priority queue for pending streaming requests. We use a priority queue to keep parts in order so that we
* can stream them to the caller in order. */
struct aws_priority_queue pending_body_streaming_requests;
/* Current state of the meta request. */
enum aws_s3_meta_request_state state;
/* The sum of initial_read_window, plus all window_increment() calls. This number never goes down. */
uint64_t read_window_running_total;
/* The next expected streaming part number needed to continue streaming part bodies. (For example, this will
* initially be 1 for part 1, and after that part is received, it will be 2, then 3, etc.. )*/
uint32_t next_streaming_part;
/* Number of parts scheduled for delivery. */
uint32_t num_parts_delivery_sent;
/* Total number of parts that have been attempted to be delivered. (Will equal the sum of succeeded and
* failed.)*/
uint32_t num_parts_delivery_completed;
/* Task for delivering events on the meta-request's io_event_loop thread.
* We do this to ensure a meta-request's callbacks are fired sequentially and non-overlapping.
* If `event_delivery_array` has items in it, then this task is scheduled.
* If `event_delivery_active` is true, then this task is actively running.
* Delivery is not 100% complete until `event_delivery_array` is empty AND `event_delivery_active` is false
* (use aws_s3_meta_request_are_events_out_for_delivery_synced() to check) */
struct aws_task event_delivery_task;
/* Array of `struct aws_s3_meta_request_event` to deliver when the `event_delivery_task` runs. */
struct aws_array_list event_delivery_array;
/* When true, events are actively being delivered to the user. */
bool event_delivery_active;
/* The end finish result of the meta request. */
struct aws_s3_meta_request_result finish_result;
/* True if the finish result has been set. */
uint32_t finish_result_set : 1;
/* To track aws_s3_requests with cancellable HTTP streams */
struct aws_linked_list cancellable_http_streams_list;
} synced_data;
/* Anything in this structure should only ever be accessed by the client on its process work event loop task. */
struct {
/* Linked list node for the meta requests linked list in the client. */
/* Note: this needs to be first for using AWS_CONTAINER_OF with the nested structure. */
struct aws_linked_list_node node;
/* True if this meta request is currently in the client's list. */
bool scheduled;
} client_process_work_threaded_data;
/* Anything in this structure should only ever be accessed by the meta-request from its io_event_loop thread. */
struct {
/* When delivering events, we swap contents with `synced_data.event_delivery_array`.
* This is an optimization, we could have just copied the array when the task runs,
* but swapping two array-lists back and forth avoids an allocation. */
struct aws_array_list event_delivery_array;
} io_threaded_data;
const bool should_compute_content_md5;
/* deep copy of the checksum config. */
struct checksum_config checksum_config;
/* checksum found in either a default get request, or in the initial head request of a multipart get */
struct aws_byte_buf meta_request_level_response_header_checksum;
/* running checksum of all the parts of a default get, or ranged get meta request*/
struct aws_s3_checksum *meta_request_level_running_response_sum;
};
/* Info for each part, that we need to remember until we send CompleteMultipartUpload */
struct aws_s3_mpu_part_info {
uint64_t size;
struct aws_string *etag;
struct aws_byte_buf checksum_base64;
bool was_previously_uploaded;
};
AWS_EXTERN_C_BEGIN
/* Initialize the base meta request structure. */
AWS_S3_API
int aws_s3_meta_request_init_base(
struct aws_allocator *allocator,
struct aws_s3_client *client,
size_t part_size,
bool should_compute_content_md5,
const struct aws_s3_meta_request_options *options,
void *impl,
struct aws_s3_meta_request_vtable *vtable,
struct aws_s3_meta_request *base_type);
/* Returns true if the meta request is still in the "active" state. */
AWS_S3_API
bool aws_s3_meta_request_is_active(struct aws_s3_meta_request *meta_request);
/* Returns true if the meta request is in the "finished" state. */
AWS_S3_API
bool aws_s3_meta_request_is_finished(struct aws_s3_meta_request *meta_request);
/* Returns true if the meta request has a finish result, which indicates that the meta request has trying to finish or
* has already finished. */
AWS_S3_API
bool aws_s3_meta_request_has_finish_result(struct aws_s3_meta_request *meta_request);
AWS_S3_API
void aws_s3_meta_request_lock_synced_data(struct aws_s3_meta_request *meta_request);
AWS_S3_API
void aws_s3_meta_request_unlock_synced_data(struct aws_s3_meta_request *meta_request);
/* Called by the client to retrieve the next request and update the meta request's internal state. out_request is
* optional, and can be NULL if just desiring to update internal state. */
AWS_S3_API
bool aws_s3_meta_request_update(
struct aws_s3_meta_request *meta_request,
uint32_t flags,
struct aws_s3_request **out_request);
AWS_S3_API
void aws_s3_meta_request_prepare_request(
struct aws_s3_meta_request *meta_request,
struct aws_s3_request *request,
aws_s3_meta_request_prepare_request_callback_fn *callback,
void *user_data);
AWS_S3_API
void aws_s3_meta_request_send_request(struct aws_s3_meta_request *meta_request, struct aws_s3_connection *connection);
AWS_S3_API
void aws_s3_meta_request_init_signing_date_time_default(
struct aws_s3_meta_request *meta_request,
struct aws_date_time *date_time);
AWS_S3_API
void aws_s3_meta_request_sign_request_default(
struct aws_s3_meta_request *meta_request,
struct aws_s3_request *request,
aws_signing_complete_fn *on_signing_complete,
void *user_data);
/* Default implementation for when a request finishes a particular send. */
AWS_S3_API
void aws_s3_meta_request_send_request_finish_default(
struct aws_s3_connection *connection,
struct aws_http_stream *stream,
int error_code);
/* Called by the client when a request is completely finished and not doing any further retries. */
AWS_S3_API
void aws_s3_meta_request_finished_request(
struct aws_s3_meta_request *meta_request,
struct aws_s3_request *request,
int error_code);
/* Called to place the request in the meta request's priority queue for streaming back to the caller. Once all requests
* with a part number less than the given request has been received, the given request and the previous requests will
* be scheduled for streaming. */
AWS_S3_API
void aws_s3_meta_request_stream_response_body_synced(
struct aws_s3_meta_request *meta_request,
struct aws_s3_request *request);
/* Add an event for delivery on the meta-request's io_event_loop thread.
* These events usually correspond to callbacks that must fire sequentially and non-overlapping,
* such as delivery of a part's response body. */
void aws_s3_meta_request_add_event_for_delivery_synced(
struct aws_s3_meta_request *meta_request,
const struct aws_s3_meta_request_event *event);
/* Returns whether any events are out for delivery.
* The meta-request's finish callback must not be invoked until this returns false. */
bool aws_s3_meta_request_are_events_out_for_delivery_synced(struct aws_s3_meta_request *meta_request);
/* Cancel the requests with cancellable HTTP stream for the meta request */
void aws_s3_meta_request_cancel_cancellable_requests_synced(struct aws_s3_meta_request *meta_request, int error_code);
/* Asynchronously read from the meta request's input stream. Should always be done outside of any mutex,
* as reading from the stream could cause user code to call back into aws-c-s3.
* This will fill the buffer to capacity, unless end of stream is reached.
* It may read from the underlying stream multiple times, if that's what it takes to fill the buffer.
* Returns a future whose result bool indicates whether end of stream was reached.
* This future may complete on any thread, and may complete synchronously.
*
* Read from offset to fill the buffer
*/
AWS_S3_API
struct aws_future_bool *aws_s3_meta_request_read_body(
struct aws_s3_meta_request *meta_request,
uint64_t offset,
struct aws_byte_buf *buffer);
bool aws_s3_meta_request_body_has_no_more_data(const struct aws_s3_meta_request *meta_request);
/* Set the meta request finish result as failed. This is meant to be called sometime before aws_s3_meta_request_finish.
* Subsequent calls to this function or to aws_s3_meta_request_set_success_synced will not overwrite the end result of
* the meta request. */
AWS_S3_API
void aws_s3_meta_request_set_fail_synced(
struct aws_s3_meta_request *meta_request,
struct aws_s3_request *failed_request,
int error_code);
/* Set the meta request finish result as successful. This is meant to be called sometime before
* aws_s3_meta_request_finish. Subsequent calls this function or to aws_s3_meta_request_set_fail_synced will not
* overwrite the end result of the meta request. */
AWS_S3_API
void aws_s3_meta_request_set_success_synced(struct aws_s3_meta_request *meta_request, int response_status);
/* Returns true if the finish result has been set (ie: either aws_s3_meta_request_set_fail_synced or
* aws_s3_meta_request_set_success_synced have been called.) */
AWS_S3_API
bool aws_s3_meta_request_has_finish_result_synced(struct aws_s3_meta_request *meta_request);
/* Virtual function called by the meta request derived type when it's completely finished and there is no other work to
* be done. */
AWS_S3_API
void aws_s3_meta_request_finish(struct aws_s3_meta_request *meta_request);
/* Default implementation of the meta request finish function. */
AWS_S3_API
void aws_s3_meta_request_finish_default(struct aws_s3_meta_request *meta_request);
/* Sets up a meta request result structure. */
AWS_S3_API
void aws_s3_meta_request_result_setup(
struct aws_s3_meta_request *meta_request,
struct aws_s3_meta_request_result *result,
struct aws_s3_request *failed_request,
int response_status,
int error_code);
/* Cleans up a meta request result structure. */
AWS_S3_API
void aws_s3_meta_request_result_clean_up(
struct aws_s3_meta_request *meta_request,
struct aws_s3_meta_request_result *result);
AWS_S3_API
bool aws_s3_meta_request_checksum_config_has_algorithm(
struct aws_s3_meta_request *meta_request,
enum aws_s3_checksum_algorithm algorithm);
AWS_EXTERN_C_END
#endif /* AWS_S3_META_REQUEST_IMPL_H */
|