xref: /aosp_15_r20/external/aws-crt-java/src/native/s3_client.c (revision 3c7ae9de214676c52d19f01067dc1a404272dc11)
1 /**
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  * SPDX-License-Identifier: Apache-2.0.
4  */
5 #include "aws_signing.h"
6 #include "credentials.h"
7 #include "crt.h"
8 #include "http_request_utils.h"
9 #include "java_class_ids.h"
10 #include "retry_utils.h"
11 #include <aws/common/string.h>
12 #include <aws/http/connection.h>
13 #include <aws/http/proxy.h>
14 #include <aws/http/request_response.h>
15 #include <aws/io/channel_bootstrap.h>
16 #include <aws/io/retry_strategy.h>
17 #include <aws/io/stream.h>
18 #include <aws/io/tls_channel_handler.h>
19 #include <aws/io/uri.h>
20 #include <aws/s3/s3_client.h>
21 #include <aws/s3/s3express_credentials_provider.h>
22 #include <http_proxy_options.h>
23 #include <http_proxy_options_environment_variable.h>
24 #include <jni.h>
25 
26 /* on 32-bit platforms, casting pointers to longs throws a warning we don't need */
27 #if UINTPTR_MAX == 0xffffffff
28 #    if defined(_MSC_VER)
29 #        pragma warning(push)
30 #        pragma warning(disable : 4305) /* 'type cast': truncation from 'jlong' to 'jni_tls_ctx_options *' */
31 #        pragma warning(disable : 4221)
32 #    else
33 #        pragma GCC diagnostic push
34 #        pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
35 #        pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
36 #    endif
37 #endif
38 
39 struct s3_client_callback_data {
40     JavaVM *jvm;
41     jobject java_s3_client;
42     struct aws_signing_config_data signing_config_data;
43     jobject java_s3express_provider_factory;
44 };
45 
46 struct s3_client_make_meta_request_callback_data {
47     JavaVM *jvm;
48     jobject java_s3_meta_request;
49     jobject java_s3_meta_request_response_handler_native_adapter;
50     struct aws_input_stream *input_stream;
51     struct aws_signing_config_data signing_config_data;
52     jthrowable java_exception;
53 };
54 
55 static void s_on_s3_client_shutdown_complete_callback(void *user_data);
56 static void s_on_s3_meta_request_shutdown_complete_callback(void *user_data);
57 
aws_s3_tcp_keep_alive_options_from_java(JNIEnv * env,jobject jni_s3_tcp_keep_alive_options,struct aws_s3_tcp_keep_alive_options * s3_tcp_keep_alive_options)58 int aws_s3_tcp_keep_alive_options_from_java(
59     JNIEnv *env,
60     jobject jni_s3_tcp_keep_alive_options,
61     struct aws_s3_tcp_keep_alive_options *s3_tcp_keep_alive_options) {
62 
63     uint16_t jni_keep_alive_interval_sec = (*env)->GetShortField(
64         env, jni_s3_tcp_keep_alive_options, s3_tcp_keep_alive_options_properties.keep_alive_interval_sec_field_id);
65 
66     uint16_t jni_keep_alive_timeout_sec = (*env)->GetShortField(
67         env, jni_s3_tcp_keep_alive_options, s3_tcp_keep_alive_options_properties.keep_alive_timeout_sec_field_id);
68 
69     uint16_t jni_keep_alive_max_failed_probes = (*env)->GetShortField(
70         env, jni_s3_tcp_keep_alive_options, s3_tcp_keep_alive_options_properties.keep_alive_max_failed_probes_field_id);
71 
72     AWS_ZERO_STRUCT(*s3_tcp_keep_alive_options);
73 
74     s3_tcp_keep_alive_options->keep_alive_interval_sec = jni_keep_alive_interval_sec;
75     s3_tcp_keep_alive_options->keep_alive_timeout_sec = jni_keep_alive_timeout_sec;
76     s3_tcp_keep_alive_options->keep_alive_max_failed_probes = jni_keep_alive_max_failed_probes;
77 
78     return AWS_OP_SUCCESS;
79 }
80 
81 struct s3_client_s3express_provider_java_impl {
82     JavaVM *jvm;
83     jobject java_s3express_provider;
84 };
85 
86 struct s3_client_s3express_provider_callback_data {
87     void *log_id;
88     aws_on_get_credentials_callback_fn *get_cred_callback;
89     void *get_cred_user_data;
90 };
91 
92 JNIEXPORT void JNICALL
Java_software_amazon_awssdk_crt_s3_S3ExpressCredentialsProvider_s3expressCredentialsProviderGetCredentialsCompleted(JNIEnv * env,jclass jni_class,jlong nativeHandler,jobject java_credentials)93     Java_software_amazon_awssdk_crt_s3_S3ExpressCredentialsProvider_s3expressCredentialsProviderGetCredentialsCompleted(
94         JNIEnv *env,
95         jclass jni_class,
96         jlong nativeHandler,
97         jobject java_credentials) {
98 
99     (void)jni_class;
100     struct s3_client_s3express_provider_callback_data *callback_data = (void *)nativeHandler;
101     struct aws_credentials *native_credentials = NULL;
102     int error_code = AWS_ERROR_SUCCESS;
103 
104     if (!java_credentials || aws_jni_check_and_clear_exception(env)) {
105         /* TODO: a separate error code?? */
106         AWS_LOGF_ERROR(
107             AWS_LS_S3_META_REQUEST,
108             "id=%p: Failed to get S3Express credentials from Java",
109             (void *)callback_data->log_id);
110         error_code = AWS_ERROR_HTTP_CALLBACK_FAILURE;
111         goto done;
112     }
113 
114     native_credentials = aws_credentials_new_from_java_credentials(env, java_credentials);
115     if (!native_credentials) {
116         aws_jni_throw_runtime_exception(env, "Failed to create native credentials");
117         AWS_LOGF_ERROR(
118             AWS_LS_S3_META_REQUEST,
119             "id=%p: Failed to create native credentials from Java",
120             (void *)callback_data->log_id);
121         goto done;
122     }
123     struct aws_byte_cursor session_token = aws_credentials_get_session_token(native_credentials);
124 
125     if (session_token.len == 0) {
126         aws_jni_throw_runtime_exception(env, "S3ExpressCredentialsProvider - sessionToken must be non-null");
127         error_code = AWS_ERROR_HTTP_CALLBACK_FAILURE;
128         aws_credentials_release(native_credentials);
129         native_credentials = NULL;
130         goto done;
131     }
132 
133 done:
134     callback_data->get_cred_callback(native_credentials, error_code, callback_data->get_cred_user_data);
135     aws_credentials_release(native_credentials);
136     aws_mem_release(aws_jni_get_allocator(), callback_data);
137 }
138 
s_s3express_get_creds_java(struct aws_s3express_credentials_provider * provider,const struct aws_credentials * original_credentials,const struct aws_credentials_properties_s3express * s3express_properties,aws_on_get_credentials_callback_fn callback,void * user_data)139 static int s_s3express_get_creds_java(
140     struct aws_s3express_credentials_provider *provider,
141     const struct aws_credentials *original_credentials,
142     const struct aws_credentials_properties_s3express *s3express_properties,
143     aws_on_get_credentials_callback_fn callback,
144     void *user_data) {
145 
146     struct s3_client_s3express_provider_java_impl *impl = provider->impl;
147     int result = AWS_OP_ERR;
148 
149     /********** JNI ENV ACQUIRE **********/
150     JNIEnv *env = aws_jni_acquire_thread_env(impl->jvm);
151     if (!env) {
152         /* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */
153         return AWS_OP_SUCCESS;
154     }
155     jobject properties_object = NULL;
156     jobject original_credentials_object = NULL;
157 
158     properties_object = (*env)->NewObject(
159         env,
160         s3express_credentials_properties_properties.s3express_credentials_properties_class,
161         s3express_credentials_properties_properties.constructor_method_id);
162     if ((*env)->ExceptionCheck(env) || properties_object == NULL) {
163         aws_jni_throw_runtime_exception(
164             env,
165             "S3ExpressCredentialsProvider.getS3ExpressCredentials: Failed to create S3ExpressCredentialsProperties "
166             "object.");
167         goto done;
168     }
169     original_credentials_object = aws_java_credentials_from_native_new(env, original_credentials);
170     if ((*env)->ExceptionCheck(env) || original_credentials_object == NULL) {
171         aws_jni_throw_runtime_exception(
172             env, "S3ExpressCredentialsProvider.getS3ExpressCredentials: Failed to create Credentials object.");
173         goto done;
174     }
175 
176     jstring jni_host = aws_jni_string_from_cursor(env, &s3express_properties->host);
177     jstring jni_region = aws_jni_string_from_cursor(env, &s3express_properties->region);
178     (*env)->SetObjectField(env, properties_object, s3express_credentials_properties_properties.host_field_id, jni_host);
179     (*env)->SetObjectField(
180         env, properties_object, s3express_credentials_properties_properties.region_field_id, jni_region);
181     (*env)->DeleteLocalRef(env, jni_host);
182     (*env)->DeleteLocalRef(env, jni_region);
183 
184     struct s3_client_s3express_provider_callback_data *callback_data =
185         aws_mem_calloc(aws_jni_get_allocator(), 1, sizeof(struct s3_client_s3express_provider_callback_data));
186 
187     callback_data->get_cred_callback = callback;
188     callback_data->get_cred_user_data = user_data;
189     callback_data->log_id = impl->java_s3express_provider;
190 
191     /* Invoke the java call */
192     (*env)->CallVoidMethod(
193         env,
194         impl->java_s3express_provider,
195         s3express_credentials_provider_properties.getS3ExpressCredentials,
196         properties_object,
197         original_credentials_object,
198         (jlong)callback_data);
199 
200     if (aws_jni_check_and_clear_exception(env)) {
201         /* Check if any exception raised */
202         AWS_LOGF_ERROR(
203             AWS_LS_S3_META_REQUEST,
204             "id=%p: S3ExpressCredentialsProvider.getS3ExpressCredentials failed",
205             (void *)impl->java_s3express_provider);
206         aws_mem_release(aws_jni_get_allocator(), callback_data);
207         goto done;
208     }
209     result = AWS_OP_SUCCESS;
210 done:
211     if (properties_object) {
212         (*env)->DeleteLocalRef(env, properties_object);
213     }
214     if (original_credentials_object) {
215         (*env)->DeleteLocalRef(env, original_credentials_object);
216     }
217     aws_jni_release_thread_env(impl->jvm, env);
218     /********** JNI ENV RELEASE **********/
219     return result;
220 }
221 
s_s3express_destroy_java(struct aws_s3express_credentials_provider * provider)222 static void s_s3express_destroy_java(struct aws_s3express_credentials_provider *provider) {
223     struct s3_client_s3express_provider_java_impl *impl = provider->impl;
224 
225     /********** JNI ENV ACQUIRE **********/
226     JNIEnv *env = aws_jni_acquire_thread_env(impl->jvm);
227     if (!env) {
228         /* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */
229         return;
230     }
231     /* Invoke the java call */
232     (*env)->CallVoidMethod(
233         env, impl->java_s3express_provider, s3express_credentials_provider_properties.destroyProvider);
234 
235     (*env)->DeleteGlobalRef(env, impl->java_s3express_provider);
236 
237     aws_jni_release_thread_env(impl->jvm, env);
238     /********** JNI ENV RELEASE **********/
239     /* Once the java call returns, the java resource should be cleaned up already. We can finish up the shutdown
240      * process. Clean up the native part. */
241     aws_simple_completion_callback *callback = provider->shutdown_complete_callback;
242     void *user_data = provider->shutdown_user_data;
243     aws_mem_release(provider->allocator, provider);
244     callback(user_data);
245 }
246 
247 static struct aws_s3express_credentials_provider_vtable s_java_s3express_vtable = {
248     .get_credentials = s_s3express_get_creds_java,
249     .destroy = s_s3express_destroy_java,
250 };
251 
s_s3express_provider_jni_factory(struct aws_allocator * allocator,struct aws_s3_client * client,aws_simple_completion_callback shutdown_complete_callback,void * shutdown_user_data,void * factory_user_data)252 struct aws_s3express_credentials_provider *s_s3express_provider_jni_factory(
253     struct aws_allocator *allocator,
254     struct aws_s3_client *client,
255     aws_simple_completion_callback shutdown_complete_callback,
256     void *shutdown_user_data,
257     void *factory_user_data) {
258 
259     (void)client;
260     struct s3_client_callback_data *client_data = factory_user_data;
261 
262     struct aws_s3express_credentials_provider *provider = NULL;
263     struct s3_client_s3express_provider_java_impl *impl = NULL;
264 
265     /********** JNI ENV ACQUIRE **********/
266     JNIEnv *env = aws_jni_acquire_thread_env(client_data->jvm);
267     if (env == NULL) {
268         /* If we can't get an environment, then the JVM is probably shutting down.  Don't crash. */
269         return NULL;
270     }
271 
272     aws_mem_acquire_many(
273         allocator,
274         2,
275         &provider,
276         sizeof(struct aws_s3express_credentials_provider),
277         &impl,
278         sizeof(struct s3_client_s3express_provider_java_impl));
279     /* Call into the java factory to create the java impl */
280     AWS_FATAL_ASSERT(client_data->java_s3express_provider_factory != NULL);
281     jobject java_s3express_provider = (*env)->CallObjectMethod(
282         env,
283         client_data->java_s3express_provider_factory,
284         s3express_credentials_provider_factory_properties.createS3ExpressCredentialsProvider,
285         client_data->java_s3_client);
286 
287     if (aws_jni_check_and_clear_exception(env)) {
288         AWS_LOGF_ERROR(
289             AWS_LS_S3_META_REQUEST,
290             "id=%p: Failed to create Java S3Express Provider",
291             (void *)client_data->java_s3express_provider_factory);
292         aws_mem_release(allocator, provider);
293         provider = NULL;
294         goto done;
295     }
296     impl->java_s3express_provider = (*env)->NewGlobalRef(env, java_s3express_provider);
297 
298     jint jvmresult = (*env)->GetJavaVM(env, &impl->jvm);
299     (void)jvmresult;
300     AWS_FATAL_ASSERT(jvmresult == 0);
301 
302     aws_s3express_credentials_provider_init_base(provider, allocator, &s_java_s3express_vtable, impl);
303     provider->shutdown_complete_callback = shutdown_complete_callback;
304     provider->shutdown_user_data = shutdown_user_data;
305 
306     /* We are done using the factory, when succeed, clean it up. TODO: we don't have to clean it up here */
307     (*env)->DeleteGlobalRef(env, client_data->java_s3express_provider_factory);
308     client_data->java_s3express_provider_factory = NULL;
309 
310 done:
311     aws_jni_release_thread_env(client_data->jvm, env);
312     /********** JNI ENV RELEASE **********/
313     return provider;
314 }
315 
Java_software_amazon_awssdk_crt_s3_S3Client_s3ClientNew(JNIEnv * env,jclass jni_class,jobject s3_client_jobject,jbyteArray jni_region,jlong jni_client_bootstrap,jlong jni_tls_ctx,jobject java_signing_config,jlong part_size_jlong,jlong multipart_upload_threshold_jlong,jdouble throughput_target_gbps,jboolean enable_read_backpressure,jlong initial_read_window_jlong,int max_connections,jobject jni_standard_retry_options,jboolean compute_content_md5,jint jni_proxy_connection_type,jbyteArray jni_proxy_host,jint jni_proxy_port,jlong jni_proxy_tls_context,jint jni_proxy_authorization_type,jbyteArray jni_proxy_authorization_username,jbyteArray jni_proxy_authorization_password,jint jni_environment_variable_proxy_connection_type,jlong jni_environment_variable_proxy_tls_connection_options,jint jni_environment_variable_type,int connect_timeout_ms,jobject jni_s3_tcp_keep_alive_options,jlong jni_monitoring_throughput_threshold_in_bytes_per_second,jint jni_monitoring_failure_interval_in_seconds,jboolean enable_s3express,jobject java_s3express_provider_factory,jlong jni_memory_limit_bytes_jlong)316 JNIEXPORT jlong JNICALL Java_software_amazon_awssdk_crt_s3_S3Client_s3ClientNew(
317     JNIEnv *env,
318     jclass jni_class,
319     jobject s3_client_jobject,
320     jbyteArray jni_region,
321     jlong jni_client_bootstrap,
322     jlong jni_tls_ctx,
323     jobject java_signing_config,
324     jlong part_size_jlong,
325     jlong multipart_upload_threshold_jlong,
326     jdouble throughput_target_gbps,
327     jboolean enable_read_backpressure,
328     jlong initial_read_window_jlong,
329     int max_connections,
330     jobject jni_standard_retry_options,
331     jboolean compute_content_md5,
332     jint jni_proxy_connection_type,
333     jbyteArray jni_proxy_host,
334     jint jni_proxy_port,
335     jlong jni_proxy_tls_context,
336     jint jni_proxy_authorization_type,
337     jbyteArray jni_proxy_authorization_username,
338     jbyteArray jni_proxy_authorization_password,
339     jint jni_environment_variable_proxy_connection_type,
340     jlong jni_environment_variable_proxy_tls_connection_options,
341     jint jni_environment_variable_type,
342     int connect_timeout_ms,
343     jobject jni_s3_tcp_keep_alive_options,
344     jlong jni_monitoring_throughput_threshold_in_bytes_per_second,
345     jint jni_monitoring_failure_interval_in_seconds,
346     jboolean enable_s3express,
347     jobject java_s3express_provider_factory,
348     jlong jni_memory_limit_bytes_jlong) {
349     (void)jni_class;
350     aws_cache_jni_ids(env);
351 
352     struct aws_allocator *allocator = aws_jni_get_allocator();
353 
354     struct aws_client_bootstrap *client_bootstrap = (struct aws_client_bootstrap *)jni_client_bootstrap;
355 
356     if (!client_bootstrap) {
357         aws_jni_throw_illegal_argument_exception(env, "Invalid Client Bootstrap");
358         return (jlong)NULL;
359     }
360 
361     uint64_t part_size = (uint64_t)part_size_jlong;
362     uint64_t multipart_upload_threshold = (uint64_t)multipart_upload_threshold_jlong;
363     uint64_t memory_limit_in_bytes = (uint64_t)jni_memory_limit_bytes_jlong;
364 
365     size_t initial_read_window;
366     if (aws_size_t_from_java(env, &initial_read_window, initial_read_window_jlong, "Initial read window")) {
367         return (jlong)NULL;
368     }
369 
370     struct aws_retry_strategy *retry_strategy = NULL;
371 
372     if (jni_standard_retry_options != NULL) {
373         struct aws_standard_retry_options retry_options;
374 
375         if (aws_standard_retry_options_from_java(env, jni_standard_retry_options, &retry_options)) {
376             return (jlong)NULL;
377         }
378 
379         if (retry_options.backoff_retry_options.el_group == NULL) {
380             retry_options.backoff_retry_options.el_group = client_bootstrap->event_loop_group;
381         }
382 
383         retry_strategy = aws_retry_strategy_new_standard(allocator, &retry_options);
384 
385         if (retry_strategy == NULL) {
386             aws_jni_throw_runtime_exception(env, "Could not create retry strategy with standard-retry-options");
387             return (jlong)NULL;
388         }
389     }
390     struct aws_s3_tcp_keep_alive_options *s3_tcp_keep_alive_options = NULL;
391 
392     if (jni_s3_tcp_keep_alive_options != NULL) {
393         s3_tcp_keep_alive_options = aws_mem_calloc(allocator, 1, sizeof(struct aws_s3_tcp_keep_alive_options));
394         if (aws_s3_tcp_keep_alive_options_from_java(env, jni_s3_tcp_keep_alive_options, s3_tcp_keep_alive_options)) {
395             aws_jni_throw_runtime_exception(env, "Could not create s3_tcp_keep_alive_options");
396             return (jlong)NULL;
397         }
398     }
399 
400     struct aws_byte_cursor region = aws_jni_byte_cursor_from_jbyteArray_acquire(env, jni_region);
401 
402     struct s3_client_callback_data *callback_data =
403         aws_mem_calloc(allocator, 1, sizeof(struct s3_client_callback_data));
404 
405     struct aws_signing_config_aws signing_config;
406     struct aws_credentials *anonymous_credentials = NULL;
407     AWS_ZERO_STRUCT(signing_config);
408     if (java_signing_config != NULL) {
409         if (aws_build_signing_config(env, java_signing_config, &callback_data->signing_config_data, &signing_config)) {
410             aws_jni_throw_runtime_exception(env, "Invalid signingConfig");
411             aws_mem_release(allocator, callback_data);
412             return (jlong)NULL;
413         }
414     } else {
415         anonymous_credentials = aws_credentials_new_anonymous(allocator);
416         signing_config.credentials = anonymous_credentials;
417     }
418 
419     if (java_s3express_provider_factory != NULL && enable_s3express) {
420         /* Create a JNI factory */
421         callback_data->java_s3express_provider_factory = (*env)->NewGlobalRef(env, java_s3express_provider_factory);
422     }
423 
424     AWS_FATAL_ASSERT(callback_data);
425     callback_data->java_s3_client = (*env)->NewGlobalRef(env, s3_client_jobject);
426 
427     jint jvmresult = (*env)->GetJavaVM(env, &callback_data->jvm);
428     (void)jvmresult;
429     AWS_FATAL_ASSERT(jvmresult == 0);
430 
431     struct aws_tls_connection_options *tls_options = NULL;
432     struct aws_tls_connection_options tls_options_storage;
433     AWS_ZERO_STRUCT(tls_options_storage);
434     if (jni_tls_ctx) {
435         struct aws_tls_ctx *tls_ctx = (void *)jni_tls_ctx;
436         tls_options = &tls_options_storage;
437         aws_tls_connection_options_init_from_ctx(tls_options, tls_ctx);
438     }
439 
440     struct aws_s3_client_config client_config = {
441         .max_active_connections_override = max_connections,
442         .region = region,
443         .client_bootstrap = client_bootstrap,
444         .tls_connection_options = tls_options,
445         .signing_config = &signing_config,
446         .part_size = part_size,
447         .multipart_upload_threshold = multipart_upload_threshold,
448         .throughput_target_gbps = throughput_target_gbps,
449         .enable_read_backpressure = enable_read_backpressure,
450         .initial_read_window = initial_read_window,
451         .retry_strategy = retry_strategy,
452         .shutdown_callback = s_on_s3_client_shutdown_complete_callback,
453         .shutdown_callback_user_data = callback_data,
454         .compute_content_md5 = compute_content_md5 ? AWS_MR_CONTENT_MD5_ENABLED : AWS_MR_CONTENT_MD5_DISABLED,
455         .connect_timeout_ms = connect_timeout_ms,
456         .tcp_keep_alive_options = s3_tcp_keep_alive_options,
457         .enable_s3express = enable_s3express,
458         .s3express_provider_override_factory =
459             java_s3express_provider_factory ? s_s3express_provider_jni_factory : NULL,
460         .factory_user_data = callback_data,
461         .memory_limit_in_bytes = memory_limit_in_bytes,
462     };
463 
464     struct aws_http_connection_monitoring_options monitoring_options;
465     AWS_ZERO_STRUCT(monitoring_options);
466     if (jni_monitoring_throughput_threshold_in_bytes_per_second >= 0 &&
467         jni_monitoring_failure_interval_in_seconds >= 2) {
468         monitoring_options.minimum_throughput_bytes_per_second =
469             jni_monitoring_throughput_threshold_in_bytes_per_second;
470         monitoring_options.allowable_throughput_failure_interval_seconds = jni_monitoring_failure_interval_in_seconds;
471         client_config.monitoring_options = &monitoring_options;
472     }
473 
474     struct aws_http_proxy_options proxy_options;
475     AWS_ZERO_STRUCT(proxy_options);
476 
477     struct aws_tls_connection_options proxy_tls_conn_options;
478     AWS_ZERO_STRUCT(proxy_tls_conn_options);
479 
480     aws_http_proxy_options_jni_init(
481         env,
482         &proxy_options,
483         jni_proxy_connection_type,
484         &proxy_tls_conn_options,
485         jni_proxy_host,
486         jni_proxy_port,
487         jni_proxy_authorization_username,
488         jni_proxy_authorization_password,
489         jni_proxy_authorization_type,
490         (struct aws_tls_ctx *)jni_proxy_tls_context);
491 
492     if (jni_proxy_host != NULL) {
493         client_config.proxy_options = &proxy_options;
494     }
495 
496     struct proxy_env_var_settings proxy_ev_settings;
497     AWS_ZERO_STRUCT(proxy_ev_settings);
498 
499     aws_http_proxy_environment_variable_setting_jni_init(
500         &proxy_ev_settings,
501         jni_environment_variable_proxy_connection_type,
502         jni_environment_variable_type,
503         (struct aws_tls_connection_options *)jni_environment_variable_proxy_tls_connection_options);
504 
505     client_config.proxy_ev_settings = &proxy_ev_settings;
506 
507     struct aws_s3_client *client = aws_s3_client_new(allocator, &client_config);
508     if (!client) {
509         aws_jni_throw_runtime_exception(env, "S3Client.aws_s3_client_new: creating aws_s3_client failed");
510         /* Clean up stuff */
511         aws_signing_config_data_clean_up(&callback_data->signing_config_data, env);
512         aws_mem_release(allocator, callback_data);
513     }
514     aws_credentials_release(anonymous_credentials);
515 
516     aws_retry_strategy_release(retry_strategy);
517 
518     aws_jni_byte_cursor_from_jbyteArray_release(env, jni_region, region);
519 
520     aws_http_proxy_options_jni_clean_up(
521         env, &proxy_options, jni_proxy_host, jni_proxy_authorization_username, jni_proxy_authorization_password);
522 
523     aws_mem_release(aws_jni_get_allocator(), s3_tcp_keep_alive_options);
524 
525     return (jlong)client;
526 }
527 
528 JNIEXPORT void JNICALL
Java_software_amazon_awssdk_crt_s3_S3Client_s3ClientDestroy(JNIEnv * env,jclass jni_class,jlong jni_s3_client)529     Java_software_amazon_awssdk_crt_s3_S3Client_s3ClientDestroy(JNIEnv *env, jclass jni_class, jlong jni_s3_client) {
530     (void)jni_class;
531     aws_cache_jni_ids(env);
532 
533     struct aws_s3_client *client = (struct aws_s3_client *)jni_s3_client;
534     if (!client) {
535         aws_jni_throw_runtime_exception(env, "S3Client.s3_client_clean_up: Invalid/null client");
536         return;
537     }
538 
539     aws_s3_client_release(client);
540 }
541 
s_on_s3_client_shutdown_complete_callback(void * user_data)542 static void s_on_s3_client_shutdown_complete_callback(void *user_data) {
543     struct s3_client_callback_data *callback = (struct s3_client_callback_data *)user_data;
544 
545     /********** JNI ENV ACQUIRE **********/
546     JNIEnv *env = aws_jni_acquire_thread_env(callback->jvm);
547     if (env == NULL) {
548         /* If we can't get an environment, then the JVM is probably shutting down.  Don't crash. */
549         return;
550     }
551 
552     AWS_LOGF_DEBUG(AWS_LS_S3_CLIENT, "S3 Client Shutdown Complete");
553     if (callback->java_s3_client != NULL) {
554         (*env)->CallVoidMethod(env, callback->java_s3_client, s3_client_properties.onShutdownComplete);
555 
556         if (aws_jni_check_and_clear_exception(env)) {
557             AWS_LOGF_ERROR(
558                 AWS_LS_S3_META_REQUEST,
559                 "id=%p: Ignored Exception from S3Client.onShutdownCompete callback",
560                 (void *)callback->java_s3_client);
561         }
562     }
563 
564     // We're done with this callback data, free it.
565     if (callback->java_s3express_provider_factory) {
566         /* Clean up factory data if still exist */
567         (*env)->DeleteGlobalRef(env, callback->java_s3express_provider_factory);
568         callback->java_s3express_provider_factory = NULL;
569     }
570     (*env)->DeleteGlobalRef(env, callback->java_s3_client);
571 
572     aws_signing_config_data_clean_up(&callback->signing_config_data, env);
573 
574     aws_jni_release_thread_env(callback->jvm, env);
575     /********** JNI ENV RELEASE **********/
576 
577     aws_mem_release(aws_jni_get_allocator(), user_data);
578 }
579 
s_on_s3_meta_request_body_callback(struct aws_s3_meta_request * meta_request,const struct aws_byte_cursor * body,uint64_t range_start,void * user_data)580 static int s_on_s3_meta_request_body_callback(
581     struct aws_s3_meta_request *meta_request,
582     const struct aws_byte_cursor *body,
583     uint64_t range_start,
584     void *user_data) {
585     (void)body;
586     (void)range_start;
587     int return_value = AWS_OP_ERR;
588 
589     uint64_t range_end = range_start + body->len;
590 
591     struct s3_client_make_meta_request_callback_data *callback_data =
592         (struct s3_client_make_meta_request_callback_data *)user_data;
593 
594     /********** JNI ENV ACQUIRE **********/
595     JNIEnv *env = aws_jni_acquire_thread_env(callback_data->jvm);
596     if (env == NULL) {
597         /* If we can't get an environment, then the JVM is probably shutting down.  Don't crash. */
598         return AWS_OP_ERR;
599     }
600 
601     jobject jni_payload = aws_jni_byte_array_from_cursor(env, body);
602     if (jni_payload == NULL) {
603         /* JVM is out of memory, but native code can still have memory available, handle it and don't crash. */
604         aws_jni_check_and_clear_exception(env);
605         aws_jni_release_thread_env(callback_data->jvm, env);
606         /********** JNI ENV RELEASE **********/
607         return aws_raise_error(AWS_ERROR_JAVA_CRT_JVM_OUT_OF_MEMORY);
608     }
609 
610     jint body_response_result = 0;
611 
612     if (callback_data->java_s3_meta_request_response_handler_native_adapter != NULL) {
613         body_response_result = (*env)->CallIntMethod(
614             env,
615             callback_data->java_s3_meta_request_response_handler_native_adapter,
616             s3_meta_request_response_handler_native_adapter_properties.onResponseBody,
617             jni_payload,
618             range_start,
619             range_end);
620 
621         if (aws_jni_get_and_clear_exception(env, &(callback_data->java_exception))) {
622             AWS_LOGF_ERROR(
623                 AWS_LS_S3_META_REQUEST,
624                 "id=%p: Received exception from S3MetaRequest.onResponseBody callback",
625                 (void *)meta_request);
626             aws_raise_error(AWS_ERROR_HTTP_CALLBACK_FAILURE);
627             goto cleanup;
628         }
629 
630         /* The Java onResponseBody API lets users return a size for auto-incrementing the read window */
631         if (body_response_result > 0) {
632             aws_s3_meta_request_increment_read_window(meta_request, (uint64_t)body_response_result);
633         }
634     }
635     return_value = AWS_OP_SUCCESS;
636 
637 cleanup:
638     (*env)->DeleteLocalRef(env, jni_payload);
639 
640     aws_jni_release_thread_env(callback_data->jvm, env);
641     /********** JNI ENV RELEASE **********/
642 
643     return return_value;
644 }
645 
s_marshal_http_headers_to_buf(const struct aws_http_headers * headers,struct aws_byte_buf * out_headers_buf)646 static int s_marshal_http_headers_to_buf(const struct aws_http_headers *headers, struct aws_byte_buf *out_headers_buf) {
647     /* calculate initial header capacity */
648     size_t headers_initial_capacity = 0;
649     for (size_t header_index = 0; header_index < aws_http_headers_count(headers); ++header_index) {
650         struct aws_http_header header;
651         aws_http_headers_get_index(headers, header_index, &header);
652         /* aws_marshal_http_headers_array_to_dynamic_buffer() impl drives this calculation */
653         headers_initial_capacity += header.name.len + header.value.len + 8;
654     }
655 
656     struct aws_allocator *allocator = aws_jni_get_allocator();
657 
658     if (aws_byte_buf_init(out_headers_buf, allocator, headers_initial_capacity)) {
659         return AWS_OP_ERR;
660     }
661 
662     if (aws_marshal_http_headers_to_dynamic_buffer(out_headers_buf, headers)) {
663         aws_byte_buf_clean_up(out_headers_buf);
664         return AWS_OP_ERR;
665     }
666 
667     return AWS_OP_SUCCESS;
668 }
669 
s_on_s3_meta_request_headers_callback(struct aws_s3_meta_request * meta_request,const struct aws_http_headers * headers,int response_status,void * user_data)670 static int s_on_s3_meta_request_headers_callback(
671     struct aws_s3_meta_request *meta_request,
672     const struct aws_http_headers *headers,
673     int response_status,
674     void *user_data) {
675     int return_value = AWS_OP_ERR;
676     struct s3_client_make_meta_request_callback_data *callback_data =
677         (struct s3_client_make_meta_request_callback_data *)user_data;
678 
679     /********** JNI ENV ACQUIRE **********/
680     JNIEnv *env = aws_jni_acquire_thread_env(callback_data->jvm);
681     if (env == NULL) {
682         /* If we can't get an environment, then the JVM is probably shutting down.  Don't crash. */
683         return AWS_OP_ERR;
684     }
685 
686     jobject java_headers_buffer = NULL;
687     struct aws_byte_buf headers_buf;
688     AWS_ZERO_STRUCT(headers_buf);
689 
690     if (s_marshal_http_headers_to_buf(headers, &headers_buf)) {
691         goto cleanup;
692     }
693 
694     java_headers_buffer = aws_jni_direct_byte_buffer_from_raw_ptr(env, headers_buf.buffer, headers_buf.len);
695     if (java_headers_buffer == NULL) {
696         aws_jni_check_and_clear_exception(env);
697         aws_raise_error(AWS_ERROR_JAVA_CRT_JVM_OUT_OF_MEMORY);
698         goto cleanup;
699     }
700 
701     if (callback_data->java_s3_meta_request_response_handler_native_adapter != NULL) {
702         (*env)->CallVoidMethod(
703             env,
704             callback_data->java_s3_meta_request_response_handler_native_adapter,
705             s3_meta_request_response_handler_native_adapter_properties.onResponseHeaders,
706             response_status,
707             java_headers_buffer);
708 
709         if (aws_jni_get_and_clear_exception(env, &(callback_data->java_exception))) {
710             AWS_LOGF_ERROR(
711                 AWS_LS_S3_META_REQUEST,
712                 "id=%p: Exception thrown from S3MetaRequest.onResponseHeaders callback",
713                 (void *)meta_request);
714 
715             aws_raise_error(AWS_ERROR_HTTP_CALLBACK_FAILURE);
716             goto cleanup;
717         }
718     }
719     return_value = AWS_OP_SUCCESS;
720 
721 cleanup:
722     aws_byte_buf_clean_up(&headers_buf);
723     if (java_headers_buffer) {
724         (*env)->DeleteLocalRef(env, java_headers_buffer);
725     }
726 
727     aws_jni_release_thread_env(callback_data->jvm, env);
728     /********** JNI ENV RELEASE **********/
729 
730     return return_value;
731 }
732 
s_on_s3_meta_request_finish_callback(struct aws_s3_meta_request * meta_request,const struct aws_s3_meta_request_result * meta_request_result,void * user_data)733 static void s_on_s3_meta_request_finish_callback(
734     struct aws_s3_meta_request *meta_request,
735     const struct aws_s3_meta_request_result *meta_request_result,
736     void *user_data) {
737 
738     (void)meta_request;
739 
740     struct s3_client_make_meta_request_callback_data *callback_data =
741         (struct s3_client_make_meta_request_callback_data *)user_data;
742 
743     /********** JNI ENV ACQUIRE **********/
744     JNIEnv *env = aws_jni_acquire_thread_env(callback_data->jvm);
745     if (env == NULL) {
746         /* If we can't get an environment, then the JVM is probably shutting down.  Don't crash. */
747         return;
748     }
749 
750     if (callback_data->java_s3_meta_request_response_handler_native_adapter != NULL) {
751         struct aws_byte_buf *error_response_body = meta_request_result->error_response_body;
752         struct aws_byte_cursor error_response_cursor;
753         AWS_ZERO_STRUCT(error_response_cursor);
754         if (error_response_body) {
755             error_response_cursor = aws_byte_cursor_from_buf(error_response_body);
756         }
757         jbyteArray jni_payload = aws_jni_byte_array_from_cursor(env, &error_response_cursor);
758         /* Only propagate java_exception if crt error code is callback failure */
759         jthrowable java_exception =
760             meta_request_result->error_code == AWS_ERROR_HTTP_CALLBACK_FAILURE ? callback_data->java_exception : NULL;
761 
762         jobject java_error_headers_buffer = NULL;
763         struct aws_byte_buf headers_buf;
764         AWS_ZERO_STRUCT(headers_buf);
765         if (meta_request_result->error_response_headers) {
766             /* Ignore any errors and just report the original error without headers */
767             if (s_marshal_http_headers_to_buf(meta_request_result->error_response_headers, &headers_buf) ==
768                 AWS_OP_SUCCESS) {
769                 java_error_headers_buffer =
770                     aws_jni_direct_byte_buffer_from_raw_ptr(env, headers_buf.buffer, headers_buf.len);
771                 if (java_error_headers_buffer == NULL) {
772                     AWS_LOGF_ERROR(
773                         AWS_LS_S3_META_REQUEST,
774                         "id=%p: Ignored Exception from "
775                         "S3MetaRequest.onFinished.aws_jni_direct_byte_buffer_from_raw_ptr",
776                         (void *)meta_request);
777                     aws_jni_check_and_clear_exception(env);
778                 }
779             } else {
780                 AWS_LOGF_ERROR(
781                     AWS_LS_S3_META_REQUEST,
782                     "id=%p: Ignored Exception from S3MetaRequest.onFinished.s_marshal_http_headers_to_buf",
783                     (void *)meta_request);
784             }
785         }
786 
787         (*env)->CallVoidMethod(
788             env,
789             callback_data->java_s3_meta_request_response_handler_native_adapter,
790             s3_meta_request_response_handler_native_adapter_properties.onFinished,
791             meta_request_result->error_code,
792             meta_request_result->response_status,
793             jni_payload,
794             meta_request_result->validation_algorithm,
795             meta_request_result->did_validate,
796             java_exception,
797             java_error_headers_buffer);
798 
799         if (aws_jni_check_and_clear_exception(env)) {
800             AWS_LOGF_ERROR(
801                 AWS_LS_S3_META_REQUEST,
802                 "id=%p: Ignored Exception from S3MetaRequest.onFinished callback",
803                 (void *)meta_request);
804         }
805         if (jni_payload) {
806             (*env)->DeleteLocalRef(env, jni_payload);
807         }
808 
809         aws_byte_buf_clean_up(&headers_buf);
810         if (java_error_headers_buffer) {
811             (*env)->DeleteLocalRef(env, java_error_headers_buffer);
812         }
813     }
814 
815     aws_jni_release_thread_env(callback_data->jvm, env);
816     /********** JNI ENV RELEASE **********/
817 }
818 
s_on_s3_meta_request_progress_callback(struct aws_s3_meta_request * meta_request,const struct aws_s3_meta_request_progress * progress,void * user_data)819 static void s_on_s3_meta_request_progress_callback(
820     struct aws_s3_meta_request *meta_request,
821     const struct aws_s3_meta_request_progress *progress,
822     void *user_data) {
823 
824     (void)meta_request;
825 
826     struct s3_client_make_meta_request_callback_data *callback_data =
827         (struct s3_client_make_meta_request_callback_data *)user_data;
828 
829     /********** JNI ENV ACQUIRE **********/
830     JNIEnv *env = aws_jni_acquire_thread_env(callback_data->jvm);
831     if (env == NULL) {
832         /* If we can't get an environment, then the JVM is probably shutting down.  Don't crash. */
833         return;
834     }
835 
836     jobject progress_object = (*env)->NewObject(
837         env,
838         s3_meta_request_progress_properties.s3_meta_request_progress_class,
839         s3_meta_request_progress_properties.s3_meta_request_progress_constructor_method_id);
840     if ((*env)->ExceptionCheck(env) || progress_object == NULL) {
841         aws_jni_throw_runtime_exception(
842             env, "S3MetaRequestResponseHandler.onProgress: Failed to create S3MetaRequestProgress object.");
843         goto done;
844     }
845 
846     (*env)->SetLongField(
847         env,
848         progress_object,
849         s3_meta_request_progress_properties.bytes_transferred_field_id,
850         progress->bytes_transferred);
851     (*env)->SetLongField(
852         env, progress_object, s3_meta_request_progress_properties.content_length_field_id, progress->content_length);
853 
854     if (callback_data->java_s3_meta_request_response_handler_native_adapter != NULL) {
855 
856         (*env)->CallVoidMethod(
857             env,
858             callback_data->java_s3_meta_request_response_handler_native_adapter,
859             s3_meta_request_response_handler_native_adapter_properties.onProgress,
860             progress_object);
861 
862         if (aws_jni_check_and_clear_exception(env)) {
863             AWS_LOGF_ERROR(
864                 AWS_LS_S3_META_REQUEST,
865                 "id=%p: Ignored Exception from S3MetaRequest.onProgress callback",
866                 (void *)meta_request);
867         }
868     }
869 
870     (*env)->DeleteLocalRef(env, progress_object);
871 
872 done:
873 
874     aws_jni_release_thread_env(callback_data->jvm, env);
875     /********** JNI ENV RELEASE **********/
876 }
877 
s_s3_meta_request_callback_cleanup(JNIEnv * env,struct s3_client_make_meta_request_callback_data * callback_data)878 static void s_s3_meta_request_callback_cleanup(
879     JNIEnv *env,
880     struct s3_client_make_meta_request_callback_data *callback_data) {
881     if (callback_data) {
882         (*env)->DeleteGlobalRef(env, callback_data->java_s3_meta_request);
883         (*env)->DeleteGlobalRef(env, callback_data->java_s3_meta_request_response_handler_native_adapter);
884         (*env)->DeleteGlobalRef(env, callback_data->java_exception);
885         aws_signing_config_data_clean_up(&callback_data->signing_config_data, env);
886         aws_mem_release(aws_jni_get_allocator(), callback_data);
887     }
888 }
889 
s_native_resume_token_from_java_new(JNIEnv * env,jobject resume_token_jni)890 static struct aws_s3_meta_request_resume_token *s_native_resume_token_from_java_new(
891     JNIEnv *env,
892     jobject resume_token_jni) {
893 
894     if (resume_token_jni == NULL) {
895         return NULL;
896     }
897 
898     struct aws_allocator *allocator = aws_jni_get_allocator();
899 
900     jint native_type =
901         (*env)->GetIntField(env, resume_token_jni, s3_meta_request_resume_token_properties.native_type_field_id);
902 
903     if (native_type != AWS_S3_META_REQUEST_TYPE_PUT_OBJECT) {
904         aws_jni_throw_illegal_argument_exception(
905             env, "ResumeToken: Operations other than PutObject are not supported for resume.");
906         return NULL;
907     }
908 
909     jlong part_size_jni =
910         (*env)->GetLongField(env, resume_token_jni, s3_meta_request_resume_token_properties.part_size_field_id);
911     jlong total_num_parts_jni =
912         (*env)->GetLongField(env, resume_token_jni, s3_meta_request_resume_token_properties.total_num_parts_field_id);
913     jlong num_parts_completed_jni = (*env)->GetLongField(
914         env, resume_token_jni, s3_meta_request_resume_token_properties.num_parts_completed_field_id);
915 
916     jstring upload_id_jni =
917         (*env)->GetObjectField(env, resume_token_jni, s3_meta_request_resume_token_properties.upload_id_field_id);
918     if (upload_id_jni == NULL) {
919         aws_jni_throw_illegal_argument_exception(env, "ResumeToken: UploadId must not be NULL.");
920         return NULL;
921     }
922 
923     struct aws_string *upload_id = aws_jni_new_string_from_jstring(env, upload_id_jni);
924 
925     struct aws_s3_upload_resume_token_options upload_options = {
926         .part_size = (uint64_t)part_size_jni,
927         .total_num_parts = (size_t)total_num_parts_jni,
928         .num_parts_completed = (size_t)num_parts_completed_jni,
929         .upload_id = aws_byte_cursor_from_string(upload_id),
930     };
931 
932     struct aws_s3_meta_request_resume_token *resume_token =
933         aws_s3_meta_request_resume_token_new_upload(allocator, &upload_options);
934     aws_string_destroy(upload_id);
935 
936     return resume_token;
937 }
938 
Java_software_amazon_awssdk_crt_s3_S3Client_s3ClientMakeMetaRequest(JNIEnv * env,jclass jni_class,jlong jni_s3_client,jobject java_s3_meta_request_jobject,jbyteArray jni_region,jint meta_request_type,jint checksum_location,jint checksum_algorithm,jboolean validate_response,jintArray jni_marshalled_validate_algorithms,jbyteArray jni_marshalled_message_data,jobject jni_http_request_body_stream,jbyteArray jni_request_filepath,jobject java_signing_config,jobject java_response_handler_jobject,jbyteArray jni_endpoint,jobject java_resume_token_jobject)939 JNIEXPORT jlong JNICALL Java_software_amazon_awssdk_crt_s3_S3Client_s3ClientMakeMetaRequest(
940     JNIEnv *env,
941     jclass jni_class,
942     jlong jni_s3_client,
943     jobject java_s3_meta_request_jobject,
944     jbyteArray jni_region,
945     jint meta_request_type,
946     jint checksum_location,
947     jint checksum_algorithm,
948     jboolean validate_response,
949     jintArray jni_marshalled_validate_algorithms,
950     jbyteArray jni_marshalled_message_data,
951     jobject jni_http_request_body_stream,
952     jbyteArray jni_request_filepath,
953     jobject java_signing_config,
954     jobject java_response_handler_jobject,
955     jbyteArray jni_endpoint,
956     jobject java_resume_token_jobject) {
957     (void)jni_class;
958     aws_cache_jni_ids(env);
959 
960     struct aws_allocator *allocator = aws_jni_get_allocator();
961     struct aws_s3_client *client = (struct aws_s3_client *)jni_s3_client;
962     struct aws_byte_cursor request_filepath;
963     AWS_ZERO_STRUCT(request_filepath);
964     struct aws_s3_meta_request_resume_token *resume_token =
965         s_native_resume_token_from_java_new(env, java_resume_token_jobject);
966     struct aws_s3_meta_request *meta_request = NULL;
967     bool success = false;
968     struct aws_byte_cursor region = aws_jni_byte_cursor_from_jbyteArray_acquire(env, jni_region);
969     struct aws_http_message *request_message = NULL;
970 
971     struct s3_client_make_meta_request_callback_data *callback_data =
972         aws_mem_calloc(allocator, 1, sizeof(struct s3_client_make_meta_request_callback_data));
973     AWS_FATAL_ASSERT(callback_data);
974     struct aws_signing_config_aws signing_config;
975     AWS_ZERO_STRUCT(signing_config);
976     if (java_signing_config != NULL) {
977         if (aws_build_signing_config(env, java_signing_config, &callback_data->signing_config_data, &signing_config)) {
978             goto done;
979         }
980     }
981 
982     jint jvmresult = (*env)->GetJavaVM(env, &callback_data->jvm);
983     (void)jvmresult;
984     AWS_FATAL_ASSERT(jvmresult == 0);
985 
986     callback_data->java_s3_meta_request = (*env)->NewGlobalRef(env, java_s3_meta_request_jobject);
987     AWS_FATAL_ASSERT(callback_data->java_s3_meta_request != NULL);
988 
989     callback_data->java_s3_meta_request_response_handler_native_adapter =
990         (*env)->NewGlobalRef(env, java_response_handler_jobject);
991     AWS_FATAL_ASSERT(callback_data->java_s3_meta_request_response_handler_native_adapter != NULL);
992 
993     request_message = aws_http_message_new_request(allocator);
994     AWS_FATAL_ASSERT(request_message);
995 
996     AWS_FATAL_ASSERT(
997         AWS_OP_SUCCESS == aws_apply_java_http_request_changes_to_native_request(
998                               env, jni_marshalled_message_data, jni_http_request_body_stream, request_message));
999 
1000     if (jni_request_filepath) {
1001         request_filepath = aws_jni_byte_cursor_from_jbyteArray_acquire(env, jni_request_filepath);
1002         if (request_filepath.ptr == NULL) {
1003             goto done;
1004         }
1005         if (request_filepath.len == 0) {
1006             aws_jni_throw_illegal_argument_exception(env, "Request file path cannot be empty");
1007             goto done;
1008         }
1009     }
1010 
1011     struct aws_uri endpoint;
1012     AWS_ZERO_STRUCT(endpoint);
1013     if (jni_endpoint != NULL) {
1014         struct aws_byte_cursor endpoint_str = aws_jni_byte_cursor_from_jbyteArray_acquire(env, jni_endpoint);
1015         int uri_parse = aws_uri_init_parse(&endpoint, allocator, &endpoint_str);
1016         aws_jni_byte_cursor_from_jbyteArray_release(env, jni_endpoint, endpoint_str);
1017         if (uri_parse) {
1018             aws_jni_throw_runtime_exception(env, "S3Client.aws_s3_client_make_meta_request: failed to parse endpoint");
1019             goto done;
1020         }
1021     }
1022 
1023     struct aws_s3_checksum_config checksum_config = {
1024         .location = checksum_location,
1025         .checksum_algorithm = checksum_algorithm,
1026         .validate_response_checksum = validate_response,
1027     };
1028 
1029     struct aws_array_list response_checksum_list;
1030     AWS_ZERO_STRUCT(response_checksum_list);
1031     if (jni_marshalled_validate_algorithms != NULL) {
1032         jint *marshalled_algorithms = (*env)->GetIntArrayElements(env, jni_marshalled_validate_algorithms, NULL);
1033         const size_t marshalled_len = (*env)->GetArrayLength(env, jni_marshalled_validate_algorithms);
1034         aws_array_list_init_dynamic(&response_checksum_list, allocator, marshalled_len, sizeof(int));
1035         for (size_t i = 0; i < marshalled_len; ++i) {
1036             enum aws_s3_checksum_algorithm algorithm = (int)marshalled_algorithms[i];
1037             aws_array_list_push_back(&response_checksum_list, &algorithm);
1038         }
1039         checksum_config.validate_checksum_algorithms = &response_checksum_list;
1040     }
1041 
1042     struct aws_s3_meta_request_options meta_request_options = {
1043         .type = meta_request_type,
1044         .checksum_config = &checksum_config,
1045         .message = request_message,
1046         .send_filepath = request_filepath,
1047         .user_data = callback_data,
1048         .signing_config = java_signing_config ? &signing_config : NULL,
1049         .headers_callback = s_on_s3_meta_request_headers_callback,
1050         .body_callback = s_on_s3_meta_request_body_callback,
1051         .finish_callback = s_on_s3_meta_request_finish_callback,
1052         .progress_callback = s_on_s3_meta_request_progress_callback,
1053         .shutdown_callback = s_on_s3_meta_request_shutdown_complete_callback,
1054         .endpoint = jni_endpoint != NULL ? &endpoint : NULL,
1055         .resume_token = resume_token,
1056     };
1057 
1058     meta_request = aws_s3_client_make_meta_request(client, &meta_request_options);
1059     /* We are done using the list, it can be safely cleaned up now. */
1060     aws_array_list_clean_up(&response_checksum_list);
1061     if (!meta_request) {
1062         aws_jni_throw_runtime_exception(
1063             env, "S3Client.aws_s3_client_make_meta_request: creating aws_s3_meta_request failed");
1064         goto done;
1065     }
1066 
1067     success = true;
1068 
1069 done:
1070     aws_s3_meta_request_resume_token_release(resume_token);
1071     aws_jni_byte_cursor_from_jbyteArray_release(env, jni_region, region);
1072     aws_http_message_release(request_message);
1073     aws_jni_byte_cursor_from_jbyteArray_release(env, jni_request_filepath, request_filepath);
1074     aws_uri_clean_up(&endpoint);
1075     if (success) {
1076         return (jlong)meta_request;
1077     }
1078     s_s3_meta_request_callback_cleanup(env, callback_data);
1079     return (jlong)0;
1080 }
1081 
s_on_s3_meta_request_shutdown_complete_callback(void * user_data)1082 static void s_on_s3_meta_request_shutdown_complete_callback(void *user_data) {
1083     struct s3_client_make_meta_request_callback_data *callback_data =
1084         (struct s3_client_make_meta_request_callback_data *)user_data;
1085 
1086     /********** JNI ENV ACQUIRE **********/
1087     JNIEnv *env = aws_jni_acquire_thread_env(callback_data->jvm);
1088     if (env == NULL) {
1089         /* If we can't get an environment, then the JVM is probably shutting down.  Don't crash. */
1090         return;
1091     }
1092 
1093     if (callback_data->java_s3_meta_request != NULL) {
1094         (*env)->CallVoidMethod(env, callback_data->java_s3_meta_request, s3_meta_request_properties.onShutdownComplete);
1095 
1096         if (aws_jni_check_and_clear_exception(env)) {
1097             AWS_LOGF_ERROR(
1098                 AWS_LS_S3_META_REQUEST,
1099                 "id=%p: Ignored Exception from S3MetaRequest.onShutdownCompete callback",
1100                 (void *)callback_data->java_s3_meta_request);
1101         }
1102     }
1103 
1104     // We're done with this callback data, free it.
1105     JavaVM *jvm = callback_data->jvm;
1106     s_s3_meta_request_callback_cleanup(env, callback_data);
1107 
1108     aws_jni_release_thread_env(jvm, env);
1109     /********** JNI ENV RELEASE **********/
1110 }
1111 
Java_software_amazon_awssdk_crt_s3_S3MetaRequest_s3MetaRequestDestroy(JNIEnv * env,jclass jni_class,jlong jni_s3_meta_request)1112 JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_s3_S3MetaRequest_s3MetaRequestDestroy(
1113     JNIEnv *env,
1114     jclass jni_class,
1115     jlong jni_s3_meta_request) {
1116     (void)jni_class;
1117     aws_cache_jni_ids(env);
1118 
1119     struct aws_s3_meta_request *meta_request = (struct aws_s3_meta_request *)jni_s3_meta_request;
1120     if (!meta_request) {
1121         aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
1122         aws_jni_throw_runtime_exception(env, "S3MetaRequest.s3MetaRequestDestroy: Invalid/null meta request");
1123         return;
1124     }
1125 
1126     aws_s3_meta_request_release(meta_request);
1127 }
1128 
Java_software_amazon_awssdk_crt_s3_S3MetaRequest_s3MetaRequestCancel(JNIEnv * env,jclass jni_class,jlong jni_s3_meta_request)1129 JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_s3_S3MetaRequest_s3MetaRequestCancel(
1130     JNIEnv *env,
1131     jclass jni_class,
1132     jlong jni_s3_meta_request) {
1133 
1134     (void)jni_class;
1135     aws_cache_jni_ids(env);
1136 
1137     struct aws_s3_meta_request *meta_request = (struct aws_s3_meta_request *)jni_s3_meta_request;
1138     if (!meta_request) {
1139         /* It's fine if this particular function does nothing when it's called
1140          * after CrtResource is closed and the handle is NULL */
1141         return;
1142     }
1143 
1144     aws_s3_meta_request_cancel(meta_request);
1145 }
1146 
Java_software_amazon_awssdk_crt_s3_S3MetaRequest_s3MetaRequestPause(JNIEnv * env,jclass jni_class,jlong jni_s3_meta_request)1147 JNIEXPORT jobject JNICALL Java_software_amazon_awssdk_crt_s3_S3MetaRequest_s3MetaRequestPause(
1148     JNIEnv *env,
1149     jclass jni_class,
1150     jlong jni_s3_meta_request) {
1151 
1152     (void)jni_class;
1153     aws_cache_jni_ids(env);
1154 
1155     struct aws_s3_meta_request *meta_request = (struct aws_s3_meta_request *)jni_s3_meta_request;
1156     if (!meta_request) {
1157         aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
1158         aws_jni_throw_illegal_argument_exception(env, "S3MetaRequest.s3MetaRequestPause: Invalid/null meta request");
1159         return NULL;
1160     }
1161 
1162     struct aws_s3_meta_request_resume_token *resume_token = NULL;
1163 
1164     if (aws_s3_meta_request_pause(meta_request, &resume_token)) {
1165         aws_jni_throw_runtime_exception(env, "S3MetaRequest.s3MetaRequestPause: Failed to pause request");
1166         return NULL;
1167     }
1168 
1169     jobject resume_token_jni = NULL;
1170     if (resume_token != NULL) {
1171         resume_token_jni = (*env)->NewObject(
1172             env,
1173             s3_meta_request_resume_token_properties.s3_meta_request_resume_token_class,
1174             s3_meta_request_resume_token_properties.s3_meta_request_resume_token_constructor_method_id);
1175         if ((*env)->ExceptionCheck(env) || resume_token_jni == NULL) {
1176             aws_jni_throw_runtime_exception(env, "S3MetaRequest.s3MetaRequestPause: Failed to create ResumeToken.");
1177             goto on_done;
1178         }
1179 
1180         enum aws_s3_meta_request_type type = aws_s3_meta_request_resume_token_type(resume_token);
1181         if (type != AWS_S3_META_REQUEST_TYPE_PUT_OBJECT) {
1182             aws_jni_throw_runtime_exception(env, "S3MetaRequest.s3MetaRequestPause: Failed to convert resume token.");
1183             goto on_done;
1184         }
1185 
1186         (*env)->SetIntField(env, resume_token_jni, s3_meta_request_resume_token_properties.native_type_field_id, type);
1187         (*env)->SetLongField(
1188             env,
1189             resume_token_jni,
1190             s3_meta_request_resume_token_properties.part_size_field_id,
1191             aws_s3_meta_request_resume_token_part_size(resume_token));
1192         (*env)->SetLongField(
1193             env,
1194             resume_token_jni,
1195             s3_meta_request_resume_token_properties.total_num_parts_field_id,
1196             aws_s3_meta_request_resume_token_total_num_parts(resume_token));
1197         (*env)->SetLongField(
1198             env,
1199             resume_token_jni,
1200             s3_meta_request_resume_token_properties.num_parts_completed_field_id,
1201             aws_s3_meta_request_resume_token_num_parts_completed(resume_token));
1202 
1203         struct aws_byte_cursor upload_id_cur = aws_s3_meta_request_resume_token_upload_id(resume_token);
1204         jstring upload_id_jni = aws_jni_string_from_cursor(env, &upload_id_cur);
1205         (*env)->SetObjectField(
1206             env, resume_token_jni, s3_meta_request_resume_token_properties.upload_id_field_id, upload_id_jni);
1207 
1208         (*env)->DeleteLocalRef(env, upload_id_jni);
1209     }
1210 
1211 on_done:
1212     aws_s3_meta_request_resume_token_release(resume_token);
1213     return resume_token_jni;
1214 }
1215 
Java_software_amazon_awssdk_crt_s3_S3MetaRequest_s3MetaRequestIncrementReadWindow(JNIEnv * env,jclass jni_class,jlong jni_s3_meta_request,jlong increment)1216 JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_s3_S3MetaRequest_s3MetaRequestIncrementReadWindow(
1217     JNIEnv *env,
1218     jclass jni_class,
1219     jlong jni_s3_meta_request,
1220     jlong increment) {
1221 
1222     (void)jni_class;
1223     aws_cache_jni_ids(env);
1224 
1225     struct aws_s3_meta_request *meta_request = (struct aws_s3_meta_request *)jni_s3_meta_request;
1226     if (!meta_request) {
1227         /* It's fine if this particular function does nothing when it's called
1228          * after CrtResource is closed and the handle is NULL */
1229         return;
1230     }
1231 
1232     if (increment < 0) {
1233         aws_jni_throw_illegal_argument_exception(
1234             env, "S3MetaRequest.s3MetaRequestIncrementReadWindow: Number cannot be negative");
1235         return;
1236     }
1237 
1238     aws_s3_meta_request_increment_read_window(meta_request, (uint64_t)increment);
1239 }
1240 
1241 #if UINTPTR_MAX == 0xffffffff
1242 #    if defined(_MSC_VER)
1243 #        pragma warning(pop)
1244 #    else
1245 #        pragma GCC diagnostic pop
1246 #    endif
1247 #endif
1248