1 /**
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0.
4 */
5
6 #include <jni.h>
7
8 #include "crt.h"
9 #include "http_connection_manager.h"
10 #include "http_request_response.h"
11 #include "http_request_utils.h"
12 #include "java_class_ids.h"
13
14 #include <aws/common/atomics.h>
15 #include <aws/common/mutex.h>
16 #include <aws/http/connection.h>
17 #include <aws/http/http.h>
18 #include <aws/http/request_response.h>
19 #include <aws/io/logging.h>
20 #include <aws/io/stream.h>
21
22 #if _MSC_VER
23 # pragma warning(disable : 4204) /* non-constant aggregate initializer */
24 #endif
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 # else
32 # pragma GCC diagnostic push
33 # pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
34 # pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
35 # endif
36 #endif
37
aws_java_http_stream_from_native_new(JNIEnv * env,void * opaque,int version)38 jobject aws_java_http_stream_from_native_new(JNIEnv *env, void *opaque, int version) {
39 jlong jni_native_ptr = (jlong)opaque;
40 AWS_ASSERT(jni_native_ptr);
41 jobject stream = NULL;
42 switch (version) {
43 case AWS_HTTP_VERSION_2:
44 stream = (*env)->NewObject(
45 env, http2_stream_properties.stream_class, http2_stream_properties.constructor, jni_native_ptr);
46 break;
47 case AWS_HTTP_VERSION_1_0:
48 case AWS_HTTP_VERSION_1_1:
49 stream = (*env)->NewObject(
50 env, http_stream_properties.stream_class, http_stream_properties.constructor, jni_native_ptr);
51 break;
52 default:
53 aws_jni_throw_runtime_exception(env, "Unsupported HTTP protocol.");
54 aws_raise_error(AWS_ERROR_UNIMPLEMENTED);
55 }
56 return stream;
57 }
58
aws_java_http_stream_from_native_delete(JNIEnv * env,jobject jHttpStream)59 void aws_java_http_stream_from_native_delete(JNIEnv *env, jobject jHttpStream) {
60 /* Delete our reference to the HttpStream Object from the JVM. */
61 (*env)->DeleteGlobalRef(env, jHttpStream);
62 }
63
64 /*******************************************************************************
65 * http_stream_binding - Jni native represent of the Java HTTP stream object
66 ******************************************************************************/
67
s_http_stream_binding_destroy(JNIEnv * env,struct http_stream_binding * binding)68 static void s_http_stream_binding_destroy(JNIEnv *env, struct http_stream_binding *binding) {
69
70 if (binding->java_http_stream_base) {
71 aws_java_http_stream_from_native_delete(env, binding->java_http_stream_base);
72 }
73
74 if (binding->java_http_response_stream_handler != NULL) {
75 (*env)->DeleteGlobalRef(env, binding->java_http_response_stream_handler);
76 }
77
78 if (binding->native_request) {
79 aws_http_message_release(binding->native_request);
80 }
81 aws_byte_buf_clean_up(&binding->headers_buf);
82 aws_mem_release(aws_jni_get_allocator(), binding);
83 }
84
aws_http_stream_binding_acquire(struct http_stream_binding * binding)85 void *aws_http_stream_binding_acquire(struct http_stream_binding *binding) {
86 if (binding == NULL) {
87 return NULL;
88 }
89 aws_atomic_fetch_add(&binding->ref, 1);
90 return binding;
91 }
92
aws_http_stream_binding_release(JNIEnv * env,struct http_stream_binding * binding)93 void *aws_http_stream_binding_release(JNIEnv *env, struct http_stream_binding *binding) {
94 if (binding == NULL) {
95 return NULL;
96 }
97 size_t pre_ref = aws_atomic_fetch_sub(&binding->ref, 1);
98 AWS_ASSERT(pre_ref > 0 && "stream binding refcount has gone negative");
99 if (pre_ref == 1) {
100 s_http_stream_binding_destroy(env, binding);
101 }
102 return NULL;
103 }
104
105 // If error occurs, A Java exception is thrown and NULL is returned.
aws_http_stream_binding_new(JNIEnv * env,jobject java_callback_handler)106 struct http_stream_binding *aws_http_stream_binding_new(JNIEnv *env, jobject java_callback_handler) {
107
108 struct aws_allocator *allocator = aws_jni_get_allocator();
109 struct http_stream_binding *binding = aws_mem_calloc(allocator, 1, sizeof(struct http_stream_binding));
110 AWS_FATAL_ASSERT(binding);
111
112 // GetJavaVM() reference doesn't need a NewGlobalRef() call since it's global by default
113 jint jvmresult = (*env)->GetJavaVM(env, &binding->jvm);
114 (void)jvmresult;
115 AWS_FATAL_ASSERT(jvmresult == 0);
116
117 binding->java_http_response_stream_handler = (*env)->NewGlobalRef(env, java_callback_handler);
118 AWS_FATAL_ASSERT(binding->java_http_response_stream_handler);
119 AWS_FATAL_ASSERT(!aws_byte_buf_init(&binding->headers_buf, allocator, 1024));
120
121 aws_atomic_init_int(&binding->ref, 1);
122
123 return binding;
124 }
125
aws_java_http_stream_on_incoming_headers_fn(struct aws_http_stream * stream,enum aws_http_header_block block_type,const struct aws_http_header * header_array,size_t num_headers,void * user_data)126 int aws_java_http_stream_on_incoming_headers_fn(
127 struct aws_http_stream *stream,
128 enum aws_http_header_block block_type,
129 const struct aws_http_header *header_array,
130 size_t num_headers,
131 void *user_data) {
132 (void)block_type;
133
134 struct http_stream_binding *binding = (struct http_stream_binding *)user_data;
135 int resp_status = -1;
136 int err_code = aws_http_stream_get_incoming_response_status(stream, &resp_status);
137 if (err_code != AWS_OP_SUCCESS) {
138 AWS_LOGF_ERROR(AWS_LS_HTTP_STREAM, "id=%p: Invalid Incoming Response Status", (void *)stream);
139 return AWS_OP_ERR;
140 }
141
142 binding->response_status = resp_status;
143
144 if (aws_marshal_http_headers_array_to_dynamic_buffer(&binding->headers_buf, header_array, num_headers)) {
145 AWS_LOGF_ERROR(
146 AWS_LS_HTTP_STREAM, "id=%p: Failed to allocate buffer space for incoming headers", (void *)stream);
147 return AWS_OP_ERR;
148 }
149
150 return AWS_OP_SUCCESS;
151 }
152
aws_java_http_stream_on_incoming_header_block_done_fn(struct aws_http_stream * stream,enum aws_http_header_block block_type,void * user_data)153 int aws_java_http_stream_on_incoming_header_block_done_fn(
154 struct aws_http_stream *stream,
155 enum aws_http_header_block block_type,
156 void *user_data) {
157 (void)stream;
158
159 struct http_stream_binding *binding = (struct http_stream_binding *)user_data;
160
161 /********** JNI ENV ACQUIRE **********/
162 JNIEnv *env = aws_jni_acquire_thread_env(binding->jvm);
163 if (env == NULL) {
164 /* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */
165 return AWS_OP_ERR;
166 }
167
168 int result = AWS_OP_ERR;
169 jint jni_block_type = block_type;
170
171 jobject jni_headers_buf =
172 aws_jni_direct_byte_buffer_from_raw_ptr(env, binding->headers_buf.buffer, binding->headers_buf.len);
173
174 (*env)->CallVoidMethod(
175 env,
176 binding->java_http_response_stream_handler,
177 http_stream_response_handler_properties.onResponseHeaders,
178 binding->java_http_stream_base,
179 (jint)binding->response_status,
180 (jint)block_type,
181 jni_headers_buf);
182
183 if (aws_jni_check_and_clear_exception(env)) {
184 (*env)->DeleteLocalRef(env, jni_headers_buf);
185 aws_raise_error(AWS_ERROR_HTTP_CALLBACK_FAILURE);
186 goto done;
187 }
188
189 /* instead of cleaning it up here, reset it in case another block is encountered */
190 aws_byte_buf_reset(&binding->headers_buf, false);
191 (*env)->DeleteLocalRef(env, jni_headers_buf);
192
193 (*env)->CallVoidMethod(
194 env,
195 binding->java_http_response_stream_handler,
196 http_stream_response_handler_properties.onResponseHeadersDone,
197 binding->java_http_stream_base,
198 jni_block_type);
199
200 if (aws_jni_check_and_clear_exception(env)) {
201 aws_raise_error(AWS_ERROR_HTTP_CALLBACK_FAILURE);
202 goto done;
203 }
204
205 result = AWS_OP_SUCCESS;
206
207 done:
208
209 aws_jni_release_thread_env(binding->jvm, env);
210 /********** JNI ENV RELEASE **********/
211
212 return result;
213 }
214
aws_java_http_stream_on_incoming_body_fn(struct aws_http_stream * stream,const struct aws_byte_cursor * data,void * user_data)215 int aws_java_http_stream_on_incoming_body_fn(
216 struct aws_http_stream *stream,
217 const struct aws_byte_cursor *data,
218 void *user_data) {
219 struct http_stream_binding *binding = (struct http_stream_binding *)user_data;
220
221 size_t total_window_increment = 0;
222
223 /********** JNI ENV ACQUIRE **********/
224 JNIEnv *env = aws_jni_acquire_thread_env(binding->jvm);
225 if (env == NULL) {
226 /* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */
227 return AWS_OP_ERR;
228 }
229
230 int result = AWS_OP_ERR;
231
232 jobject jni_payload = aws_jni_direct_byte_buffer_from_raw_ptr(env, data->ptr, data->len);
233
234 jint window_increment = (*env)->CallIntMethod(
235 env,
236 binding->java_http_response_stream_handler,
237 http_stream_response_handler_properties.onResponseBody,
238 binding->java_http_stream_base,
239 jni_payload);
240
241 (*env)->DeleteLocalRef(env, jni_payload);
242
243 if (aws_jni_check_and_clear_exception(env)) {
244 AWS_LOGF_ERROR(AWS_LS_HTTP_STREAM, "id=%p: Received Exception from onResponseBody", (void *)stream);
245 aws_raise_error(AWS_ERROR_HTTP_CALLBACK_FAILURE);
246 goto done;
247 }
248
249 if (window_increment < 0) {
250 AWS_LOGF_ERROR(AWS_LS_HTTP_STREAM, "id=%p: Window Increment from onResponseBody < 0", (void *)stream);
251 aws_raise_error(AWS_ERROR_HTTP_CALLBACK_FAILURE);
252 goto done;
253 }
254
255 total_window_increment += window_increment;
256
257 if (total_window_increment > 0) {
258 aws_http_stream_update_window(stream, total_window_increment);
259 }
260
261 result = AWS_OP_SUCCESS;
262
263 done:
264
265 aws_jni_release_thread_env(binding->jvm, env);
266 /********** JNI ENV RELEASE **********/
267
268 return result;
269 }
270
aws_java_http_stream_on_stream_complete_fn(struct aws_http_stream * stream,int error_code,void * user_data)271 void aws_java_http_stream_on_stream_complete_fn(struct aws_http_stream *stream, int error_code, void *user_data) {
272 struct http_stream_binding *binding = (struct http_stream_binding *)user_data;
273
274 /********** JNI ENV ACQUIRE **********/
275 JNIEnv *env = aws_jni_acquire_thread_env(binding->jvm);
276 if (env == NULL) {
277 /* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */
278 return;
279 }
280
281 /* Don't invoke Java callbacks if Java HttpStream failed to completely setup */
282 jint jErrorCode = error_code;
283 (*env)->CallVoidMethod(
284 env,
285 binding->java_http_response_stream_handler,
286 http_stream_response_handler_properties.onResponseComplete,
287 binding->java_http_stream_base,
288 jErrorCode);
289
290 if (aws_jni_check_and_clear_exception(env)) {
291 /* Close the Connection if the Java Callback throws an Exception */
292 aws_http_connection_close(aws_http_stream_get_connection(stream));
293 }
294
295 aws_jni_release_thread_env(binding->jvm, env);
296 /********** JNI ENV RELEASE **********/
297 }
298
aws_java_http_stream_on_stream_destroy_fn(void * user_data)299 void aws_java_http_stream_on_stream_destroy_fn(void *user_data) {
300 struct http_stream_binding *binding = (struct http_stream_binding *)user_data;
301
302 /********** JNI ENV ACQUIRE **********/
303 JNIEnv *env = aws_jni_acquire_thread_env(binding->jvm);
304 if (env == NULL) {
305 /* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */
306 return;
307 }
308 /* Native stream destroyed, release the binding. */
309 aws_http_stream_binding_release(env, binding);
310 aws_jni_release_thread_env(binding->jvm, env);
311 /********** JNI ENV RELEASE **********/
312 }
313
aws_java_http_stream_on_stream_metrics_fn(struct aws_http_stream * stream,const struct aws_http_stream_metrics * metrics,void * user_data)314 void aws_java_http_stream_on_stream_metrics_fn(
315 struct aws_http_stream *stream,
316 const struct aws_http_stream_metrics *metrics,
317 void *user_data) {
318 struct http_stream_binding *binding = (struct http_stream_binding *)user_data;
319
320 /********** JNI ENV ACQUIRE **********/
321 JNIEnv *env = aws_jni_acquire_thread_env(binding->jvm);
322 if (env == NULL) {
323 /* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */
324 return;
325 }
326
327 /* Convert metrics to Java HttpStreamMetrics obj */
328 jobject jni_metrics = (*env)->NewObject(
329 env,
330 http_stream_metrics_properties.http_stream_metrics_class,
331 http_stream_metrics_properties.constructor_id,
332 (jlong)metrics->send_start_timestamp_ns,
333 (jlong)metrics->send_end_timestamp_ns,
334 (jlong)metrics->sending_duration_ns,
335 (jlong)metrics->receive_start_timestamp_ns,
336 (jlong)metrics->receive_end_timestamp_ns,
337 (jlong)metrics->receiving_duration_ns,
338
339 /* Stream IDs are 31-bit unsigned integers, which fits into Java's regular (signed) 32-bit int */
340 (jint)metrics->stream_id);
341
342 (*env)->CallVoidMethod(
343 env,
344 binding->java_http_response_stream_handler,
345 http_stream_response_handler_properties.onMetrics,
346 binding->java_http_stream_base,
347 jni_metrics);
348
349 /* Delete local reference to metrics object */
350 (*env)->DeleteLocalRef(env, jni_metrics);
351
352 if (aws_jni_check_and_clear_exception(env)) {
353 /* Close the Connection if the Java Callback throws an Exception */
354 aws_http_connection_close(aws_http_stream_get_connection(stream));
355
356 AWS_LOGF_ERROR(AWS_LS_HTTP_STREAM, "id=%p: Received Exception from onMetrics", (void *)stream);
357 aws_raise_error(AWS_ERROR_HTTP_CALLBACK_FAILURE);
358 }
359
360 aws_jni_release_thread_env(binding->jvm, env);
361 /********** JNI ENV RELEASE **********/
362 }
363
aws_java_http_headers_from_native(JNIEnv * env,struct aws_http_headers * headers)364 jobjectArray aws_java_http_headers_from_native(JNIEnv *env, struct aws_http_headers *headers) {
365 (void)headers;
366 jobjectArray ret;
367 const size_t header_count = aws_http_headers_count(headers);
368
369 ret = (jobjectArray)(*env)->NewObjectArray(
370 env, (jsize)header_count, http_header_properties.http_header_class, (void *)NULL);
371
372 for (size_t index = 0; index < header_count; index += 1) {
373 struct aws_http_header header;
374 aws_http_headers_get_index(headers, index, &header);
375 jbyteArray header_name = aws_jni_byte_array_from_cursor(env, &header.name);
376 jbyteArray header_value = aws_jni_byte_array_from_cursor(env, &header.value);
377
378 jobject java_http_header = (*env)->NewObject(
379 env,
380 http_header_properties.http_header_class,
381 http_header_properties.constructor_method_id,
382 header_name,
383 header_value);
384
385 (*env)->SetObjectArrayElement(env, ret, (jsize)index, java_http_header);
386 }
387
388 return (ret);
389 }
390
s_make_request_general(JNIEnv * env,jlong jni_connection,jbyteArray marshalled_request,jobject jni_http_request_body_stream,jobject jni_http_response_callback_handler,enum aws_http_version version)391 static jobject s_make_request_general(
392 JNIEnv *env,
393 jlong jni_connection,
394 jbyteArray marshalled_request,
395 jobject jni_http_request_body_stream,
396 jobject jni_http_response_callback_handler,
397 enum aws_http_version version) {
398
399 struct aws_http_connection_binding *connection_binding = (struct aws_http_connection_binding *)jni_connection;
400 struct aws_http_connection *native_conn = connection_binding->connection;
401
402 if (!native_conn) {
403 aws_jni_throw_null_pointer_exception(env, "HttpClientConnection.MakeRequest: Invalid aws_http_connection");
404 return (jobject)NULL;
405 }
406
407 if (!jni_http_response_callback_handler) {
408 aws_jni_throw_illegal_argument_exception(
409 env, "HttpClientConnection.MakeRequest: Invalid jni_http_response_callback_handler");
410 return (jobject)NULL;
411 }
412
413 /* initial refcount created for the Java object */
414 struct http_stream_binding *stream_binding = aws_http_stream_binding_new(env, jni_http_response_callback_handler);
415 if (!stream_binding) {
416 /* Exception already thrown */
417 return (jobject)NULL;
418 }
419
420 stream_binding->native_request =
421 aws_http_request_new_from_java_http_request(env, marshalled_request, jni_http_request_body_stream);
422 if (stream_binding->native_request == NULL) {
423 /* Exception already thrown */
424 goto error;
425 }
426
427 struct aws_http_make_request_options request_options = {
428 .self_size = sizeof(request_options),
429 .request = stream_binding->native_request,
430 /* Set Callbacks */
431 .on_response_headers = aws_java_http_stream_on_incoming_headers_fn,
432 .on_response_header_block_done = aws_java_http_stream_on_incoming_header_block_done_fn,
433 .on_response_body = aws_java_http_stream_on_incoming_body_fn,
434 .on_complete = aws_java_http_stream_on_stream_complete_fn,
435 .on_destroy = aws_java_http_stream_on_stream_destroy_fn,
436 .on_metrics = aws_java_http_stream_on_stream_metrics_fn,
437 .user_data = stream_binding,
438 };
439
440 stream_binding->native_stream = aws_http_connection_make_request(native_conn, &request_options);
441 if (stream_binding->native_stream == NULL) {
442 AWS_LOGF_ERROR(AWS_LS_HTTP_CONNECTION, "Stream Request Failed. conn: %p", (void *)native_conn);
443 aws_jni_throw_runtime_exception(env, "HttpClientConnection.MakeRequest: Unable to Execute Request");
444 goto error;
445 }
446
447 /* Stream created successfully, acquire on binding for the native stream lifetime. */
448 aws_http_stream_binding_acquire(stream_binding);
449
450 jobject jHttpStreamBase = aws_java_http_stream_from_native_new(env, stream_binding, version);
451 if (jHttpStreamBase == NULL) {
452 goto error;
453 }
454
455 AWS_LOGF_TRACE(
456 AWS_LS_HTTP_CONNECTION,
457 "Opened new Stream on Connection. conn: %p, stream: %p",
458 (void *)native_conn,
459 (void *)stream_binding->native_stream);
460
461 return jHttpStreamBase;
462
463 error:
464 aws_http_stream_release(stream_binding->native_stream);
465 aws_http_stream_binding_release(env, stream_binding);
466 return NULL;
467 }
468
Java_software_amazon_awssdk_crt_http_HttpClientConnection_httpClientConnectionMakeRequest(JNIEnv * env,jclass jni_class,jlong jni_connection,jbyteArray marshalled_request,jobject jni_http_request_body_stream,jobject jni_http_response_callback_handler)469 JNIEXPORT jobject JNICALL Java_software_amazon_awssdk_crt_http_HttpClientConnection_httpClientConnectionMakeRequest(
470 JNIEnv *env,
471 jclass jni_class,
472 jlong jni_connection,
473 jbyteArray marshalled_request,
474 jobject jni_http_request_body_stream,
475 jobject jni_http_response_callback_handler) {
476 (void)jni_class;
477 aws_cache_jni_ids(env);
478
479 return s_make_request_general(
480 env,
481 jni_connection,
482 marshalled_request,
483 jni_http_request_body_stream,
484 jni_http_response_callback_handler,
485 AWS_HTTP_VERSION_1_1);
486 }
487
Java_software_amazon_awssdk_crt_http_Http2ClientConnection_http2ClientConnectionMakeRequest(JNIEnv * env,jclass jni_class,jlong jni_connection,jbyteArray marshalled_request,jobject jni_http_request_body_stream,jobject jni_http_response_callback_handler)488 JNIEXPORT jobject JNICALL Java_software_amazon_awssdk_crt_http_Http2ClientConnection_http2ClientConnectionMakeRequest(
489 JNIEnv *env,
490 jclass jni_class,
491 jlong jni_connection,
492 jbyteArray marshalled_request,
493 jobject jni_http_request_body_stream,
494 jobject jni_http_response_callback_handler) {
495 (void)jni_class;
496 aws_cache_jni_ids(env);
497
498 return s_make_request_general(
499 env,
500 jni_connection,
501 marshalled_request,
502 jni_http_request_body_stream,
503 jni_http_response_callback_handler,
504 AWS_HTTP_VERSION_2);
505 }
506
507 struct http_stream_chunked_callback_data {
508 struct http_stream_binding *stream_cb_data;
509 struct aws_byte_buf chunk_data;
510 struct aws_input_stream *chunk_stream;
511 jobject completion_callback;
512 };
513
s_cleanup_chunked_callback_data(JNIEnv * env,struct http_stream_chunked_callback_data * chunked_callback_data)514 static void s_cleanup_chunked_callback_data(
515 JNIEnv *env,
516 struct http_stream_chunked_callback_data *chunked_callback_data) {
517 aws_input_stream_destroy(chunked_callback_data->chunk_stream);
518 aws_byte_buf_clean_up(&chunked_callback_data->chunk_data);
519 (*env)->DeleteGlobalRef(env, chunked_callback_data->completion_callback);
520 aws_mem_release(aws_jni_get_allocator(), chunked_callback_data);
521 }
522
s_write_chunk_complete(struct aws_http_stream * stream,int error_code,void * user_data)523 static void s_write_chunk_complete(struct aws_http_stream *stream, int error_code, void *user_data) {
524 (void)stream;
525
526 struct http_stream_chunked_callback_data *chunked_callback_data = user_data;
527
528 /********** JNI ENV ACQUIRE **********/
529 JNIEnv *env = aws_jni_acquire_thread_env(chunked_callback_data->stream_cb_data->jvm);
530 if (env == NULL) {
531 /* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */
532 return;
533 }
534
535 (*env)->CallVoidMethod(
536 env,
537 chunked_callback_data->completion_callback,
538 http_stream_write_chunk_completion_properties.callback,
539 error_code);
540 aws_jni_check_and_clear_exception(env);
541
542 JavaVM *jvm = chunked_callback_data->stream_cb_data->jvm;
543 s_cleanup_chunked_callback_data(env, chunked_callback_data);
544 aws_jni_release_thread_env(jvm, env);
545 /********** JNI ENV RELEASE **********/
546 }
547
Java_software_amazon_awssdk_crt_http_HttpStream_httpStreamWriteChunk(JNIEnv * env,jclass jni_class,jlong jni_cb_data,jbyteArray chunk_data,jboolean is_final_chunk,jobject completion_callback)548 JNIEXPORT jint JNICALL Java_software_amazon_awssdk_crt_http_HttpStream_httpStreamWriteChunk(
549 JNIEnv *env,
550 jclass jni_class,
551 jlong jni_cb_data,
552 jbyteArray chunk_data,
553 jboolean is_final_chunk,
554 jobject completion_callback) {
555 (void)jni_class;
556 aws_cache_jni_ids(env);
557
558 struct http_stream_binding *cb_data = (struct http_stream_binding *)jni_cb_data;
559 struct aws_http_stream *stream = cb_data->native_stream;
560
561 struct http_stream_chunked_callback_data *chunked_callback_data =
562 aws_mem_calloc(aws_jni_get_allocator(), 1, sizeof(struct http_stream_chunked_callback_data));
563
564 chunked_callback_data->stream_cb_data = cb_data;
565 chunked_callback_data->completion_callback = (*env)->NewGlobalRef(env, completion_callback);
566
567 struct aws_byte_cursor chunk_cur = aws_jni_byte_cursor_from_jbyteArray_acquire(env, chunk_data);
568 aws_byte_buf_init_copy_from_cursor(&chunked_callback_data->chunk_data, aws_jni_get_allocator(), chunk_cur);
569 aws_jni_byte_cursor_from_jbyteArray_release(env, chunk_data, chunk_cur);
570
571 struct aws_http1_chunk_options chunk_options = {
572 .chunk_data_size = chunked_callback_data->chunk_data.len,
573 .user_data = chunked_callback_data,
574 .on_complete = s_write_chunk_complete,
575 };
576
577 chunk_cur = aws_byte_cursor_from_buf(&chunked_callback_data->chunk_data);
578 chunked_callback_data->chunk_stream = aws_input_stream_new_from_cursor(aws_jni_get_allocator(), &chunk_cur);
579 chunk_options.chunk_data = chunked_callback_data->chunk_stream;
580
581 if (aws_http1_stream_write_chunk(stream, &chunk_options)) {
582 s_cleanup_chunked_callback_data(env, chunked_callback_data);
583 return AWS_OP_ERR;
584 }
585
586 if (is_final_chunk) {
587 struct aws_http1_chunk_options final_chunk_options = {
588 .chunk_data_size = 0,
589 };
590
591 if (aws_http1_stream_write_chunk(stream, &final_chunk_options)) {
592 return AWS_OP_ERR;
593 }
594 }
595
596 return AWS_OP_SUCCESS;
597 }
598
Java_software_amazon_awssdk_crt_http_HttpStreamBase_httpStreamBaseActivate(JNIEnv * env,jclass jni_class,jlong jni_stream_binding,jobject j_http_stream_base)599 JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_http_HttpStreamBase_httpStreamBaseActivate(
600 JNIEnv *env,
601 jclass jni_class,
602 jlong jni_stream_binding,
603 jobject j_http_stream_base) {
604 (void)jni_class;
605 aws_cache_jni_ids(env);
606
607 struct http_stream_binding *binding = (struct http_stream_binding *)jni_stream_binding;
608 struct aws_http_stream *stream = binding->native_stream;
609
610 if (stream == NULL) {
611 aws_jni_throw_runtime_exception(env, "HttpStream is null.");
612 return;
613 }
614
615 AWS_LOGF_TRACE(AWS_LS_HTTP_STREAM, "Activating Stream. stream: %p", (void *)stream);
616
617 /* global ref this because now the callbacks will be firing, and they will release their reference when the
618 * stream callback sequence completes. */
619 binding->java_http_stream_base = (*env)->NewGlobalRef(env, j_http_stream_base);
620 if (aws_http_stream_activate(stream)) {
621 (*env)->DeleteGlobalRef(env, binding->java_http_stream_base);
622 aws_jni_throw_runtime_exception(
623 env, "HttpStream activate failed with error %s\n", aws_error_str(aws_last_error()));
624 }
625 }
626
Java_software_amazon_awssdk_crt_http_HttpStreamBase_httpStreamBaseRelease(JNIEnv * env,jclass jni_class,jlong jni_binding)627 JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_http_HttpStreamBase_httpStreamBaseRelease(
628 JNIEnv *env,
629 jclass jni_class,
630 jlong jni_binding) {
631
632 (void)jni_class;
633 aws_cache_jni_ids(env);
634
635 struct http_stream_binding *binding = (struct http_stream_binding *)jni_binding;
636 struct aws_http_stream *stream = binding->native_stream;
637
638 if (stream == NULL) {
639 aws_jni_throw_runtime_exception(env, "HttpStream is null.");
640 return;
641 }
642 AWS_LOGF_TRACE(AWS_LS_HTTP_STREAM, "Releasing Stream. stream: %p", (void *)stream);
643 aws_http_stream_release(stream);
644
645 aws_http_stream_binding_release(env, binding);
646 }
647
Java_software_amazon_awssdk_crt_http_HttpStreamBase_httpStreamBaseGetResponseStatusCode(JNIEnv * env,jclass jni_class,jlong jni_binding)648 JNIEXPORT jint JNICALL Java_software_amazon_awssdk_crt_http_HttpStreamBase_httpStreamBaseGetResponseStatusCode(
649 JNIEnv *env,
650 jclass jni_class,
651 jlong jni_binding) {
652
653 (void)jni_class;
654 aws_cache_jni_ids(env);
655
656 struct http_stream_binding *binding = (struct http_stream_binding *)jni_binding;
657 struct aws_http_stream *stream = binding->native_stream;
658
659 if (stream == NULL) {
660 aws_jni_throw_runtime_exception(env, "HttpStream is null.");
661 return -1;
662 }
663
664 int status = -1;
665 int err_code = aws_http_stream_get_incoming_response_status(stream, &status);
666
667 if (err_code != AWS_OP_SUCCESS) {
668 aws_jni_throw_runtime_exception(env, "Error Getting Response Status Code from HttpStream.");
669 return -1;
670 }
671
672 return (jint)status;
673 }
674
Java_software_amazon_awssdk_crt_http_HttpStreamBase_httpStreamBaseIncrementWindow(JNIEnv * env,jclass jni_class,jlong jni_binding,jint window_update)675 JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_http_HttpStreamBase_httpStreamBaseIncrementWindow(
676 JNIEnv *env,
677 jclass jni_class,
678 jlong jni_binding,
679 jint window_update) {
680
681 (void)jni_class;
682 aws_cache_jni_ids(env);
683
684 struct http_stream_binding *binding = (struct http_stream_binding *)jni_binding;
685 struct aws_http_stream *stream = binding->native_stream;
686
687 if (stream == NULL) {
688 aws_jni_throw_runtime_exception(env, "HttpStream is null.");
689 return;
690 }
691
692 if (window_update < 0) {
693 aws_jni_throw_runtime_exception(env, "Window Update is < 0");
694 return;
695 }
696
697 AWS_LOGF_TRACE(
698 AWS_LS_HTTP_STREAM, "Updating Stream Window. stream: %p, update: %d", (void *)stream, (int)window_update);
699 aws_http_stream_update_window(stream, window_update);
700 }
701
Java_software_amazon_awssdk_crt_http_Http2Stream_http2StreamResetStream(JNIEnv * env,jclass jni_class,jlong jni_cb_data,jint error_code)702 JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_http_Http2Stream_http2StreamResetStream(
703 JNIEnv *env,
704 jclass jni_class,
705 jlong jni_cb_data,
706 jint error_code) {
707
708 (void)jni_class;
709 aws_cache_jni_ids(env);
710
711 struct http_stream_binding *binding = (struct http_stream_binding *)jni_cb_data;
712 struct aws_http_stream *stream = binding->native_stream;
713
714 if (stream == NULL) {
715 aws_jni_throw_null_pointer_exception(env, "Http2Stream is null.");
716 return;
717 }
718
719 AWS_LOGF_TRACE(AWS_LS_HTTP_STREAM, "Resetting Stream. stream: %p", (void *)stream);
720 if (aws_http2_stream_reset(stream, error_code)) {
721 aws_jni_throw_runtime_exception(
722 env, "reset stream failed with error %d(%s).", aws_last_error(), aws_error_debug_str(aws_last_error()));
723 return;
724 }
725 }
726
Java_software_amazon_awssdk_crt_http_HttpClientConnection_httpClientConnectionShutdown(JNIEnv * env,jclass jni_class,jlong jni_connection)727 JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_http_HttpClientConnection_httpClientConnectionShutdown(
728 JNIEnv *env,
729 jclass jni_class,
730 jlong jni_connection) {
731
732 (void)jni_class;
733 aws_cache_jni_ids(env);
734
735 struct aws_http_connection_binding *connection_binding = (struct aws_http_connection_binding *)jni_connection;
736 struct aws_http_connection *native_conn = connection_binding->connection;
737
738 if (!native_conn) {
739 aws_jni_throw_runtime_exception(env, "HttpClientConnection.Shutdown: Invalid aws_http_connection");
740 return;
741 }
742
743 aws_http_connection_close(native_conn);
744 }
745
Java_software_amazon_awssdk_crt_http_HttpClientConnection_httpClientConnectionIsOpen(JNIEnv * env,jclass jni_class,jlong jni_connection)746 JNIEXPORT jboolean JNICALL Java_software_amazon_awssdk_crt_http_HttpClientConnection_httpClientConnectionIsOpen(
747 JNIEnv *env,
748 jclass jni_class,
749 jlong jni_connection) {
750
751 (void)jni_class;
752 aws_cache_jni_ids(env);
753
754 struct aws_http_connection_binding *connection_binding = (struct aws_http_connection_binding *)jni_connection;
755 struct aws_http_connection *native_conn = connection_binding->connection;
756
757 if (!native_conn) {
758 aws_jni_throw_runtime_exception(env, "HttpClientConnection.isOpen: Invalid aws_http_connection");
759 return false;
760 }
761
762 return aws_http_connection_is_open(native_conn);
763 }
764
Java_software_amazon_awssdk_crt_http_HttpClientConnection_httpClientConnectionGetVersion(JNIEnv * env,jclass jni_class,jlong jni_connection)765 JNIEXPORT jshort JNICALL Java_software_amazon_awssdk_crt_http_HttpClientConnection_httpClientConnectionGetVersion(
766 JNIEnv *env,
767 jclass jni_class,
768 jlong jni_connection) {
769
770 (void)jni_class;
771 aws_cache_jni_ids(env);
772
773 struct aws_http_connection_binding *connection_binding = (struct aws_http_connection_binding *)jni_connection;
774 struct aws_http_connection *native_conn = connection_binding->connection;
775
776 if (!native_conn) {
777 aws_jni_throw_runtime_exception(env, "HttpClientConnection.getVersion: Invalid aws_http_connection");
778 return 0;
779 }
780 return (jshort)aws_http_connection_get_version(native_conn);
781 }
782
Java_software_amazon_awssdk_crt_http_HttpClientConnection_isErrorRetryable(JNIEnv * env,jclass jni_class,jint error_code)783 JNIEXPORT jboolean JNICALL Java_software_amazon_awssdk_crt_http_HttpClientConnection_isErrorRetryable(
784 JNIEnv *env,
785 jclass jni_class,
786 jint error_code) {
787
788 (void)jni_class;
789 (void)env;
790 aws_cache_jni_ids(env);
791
792 switch (error_code) {
793 case AWS_ERROR_HTTP_HEADER_NOT_FOUND:
794 case AWS_ERROR_HTTP_INVALID_HEADER_FIELD:
795 case AWS_ERROR_HTTP_INVALID_HEADER_NAME:
796 case AWS_ERROR_HTTP_INVALID_HEADER_VALUE:
797 case AWS_ERROR_HTTP_INVALID_METHOD:
798 case AWS_ERROR_HTTP_INVALID_PATH:
799 case AWS_ERROR_HTTP_INVALID_STATUS_CODE:
800 case AWS_ERROR_HTTP_MISSING_BODY_STREAM:
801 case AWS_ERROR_HTTP_INVALID_BODY_STREAM:
802 case AWS_ERROR_HTTP_OUTGOING_STREAM_LENGTH_INCORRECT:
803 case AWS_ERROR_HTTP_CALLBACK_FAILURE:
804 case AWS_ERROR_HTTP_STREAM_MANAGER_SHUTTING_DOWN:
805 case AWS_HTTP2_ERR_CANCEL:
806 return false;
807 default:
808 return true;
809 }
810 }
811
812 struct aws_http2_callback_data {
813 JavaVM *jvm;
814 jobject async_callback;
815 };
816
s_cleanup_http2_callback_data(struct aws_http2_callback_data * callback_data,JNIEnv * env)817 static void s_cleanup_http2_callback_data(struct aws_http2_callback_data *callback_data, JNIEnv *env) {
818 if (callback_data == NULL || env == NULL) {
819 return;
820 }
821
822 if (callback_data->async_callback) {
823 (*env)->DeleteGlobalRef(env, callback_data->async_callback);
824 }
825
826 aws_mem_release(aws_jni_get_allocator(), callback_data);
827 }
828
s_new_http2_callback_data(JNIEnv * env,struct aws_allocator * allocator,jobject async_callback)829 static struct aws_http2_callback_data *s_new_http2_callback_data(
830 JNIEnv *env,
831 struct aws_allocator *allocator,
832 jobject async_callback) {
833 struct aws_http2_callback_data *callback_data =
834 aws_mem_calloc(allocator, 1, sizeof(struct aws_http2_callback_data));
835
836 jint jvmresult = (*env)->GetJavaVM(env, &callback_data->jvm);
837 AWS_FATAL_ASSERT(jvmresult == 0);
838 callback_data->async_callback = async_callback ? (*env)->NewGlobalRef(env, async_callback) : NULL;
839 AWS_FATAL_ASSERT(callback_data->async_callback != NULL);
840
841 return callback_data;
842 }
843
s_on_settings_completed(struct aws_http_connection * http2_connection,int error_code,void * user_data)844 static void s_on_settings_completed(struct aws_http_connection *http2_connection, int error_code, void *user_data) {
845 (void)http2_connection;
846 struct aws_http2_callback_data *callback_data = user_data;
847
848 /********** JNI ENV ACQUIRE **********/
849 JavaVM *jvm = callback_data->jvm;
850 JNIEnv *env = aws_jni_acquire_thread_env(jvm);
851 if (env == NULL) {
852 /* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */
853 return;
854 }
855
856 if (error_code) {
857 jobject crt_exception = aws_jni_new_crt_exception_from_error_code(env, error_code);
858 (*env)->CallVoidMethod(env, callback_data->async_callback, async_callback_properties.on_failure, crt_exception);
859 (*env)->DeleteLocalRef(env, crt_exception);
860 } else {
861 (*env)->CallVoidMethod(env, callback_data->async_callback, async_callback_properties.on_success);
862 }
863 AWS_FATAL_ASSERT(!aws_jni_check_and_clear_exception(env));
864 s_cleanup_http2_callback_data(callback_data, env);
865
866 aws_jni_release_thread_env(jvm, env);
867 /********** JNI ENV RELEASE **********/
868 }
869
Java_software_amazon_awssdk_crt_http_Http2ClientConnection_http2ClientConnectionUpdateSettings(JNIEnv * env,jclass jni_class,jlong jni_connection,jobject java_async_callback,jlongArray java_marshalled_settings)870 JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_http_Http2ClientConnection_http2ClientConnectionUpdateSettings(
871 JNIEnv *env,
872 jclass jni_class,
873 jlong jni_connection,
874 jobject java_async_callback,
875 jlongArray java_marshalled_settings) {
876
877 (void)jni_class;
878 aws_cache_jni_ids(env);
879
880 struct aws_http_connection_binding *connection_binding = (struct aws_http_connection_binding *)jni_connection;
881 struct aws_http_connection *native_conn = connection_binding->connection;
882
883 if (!native_conn) {
884 aws_jni_throw_null_pointer_exception(
885 env, "Http2ClientConnection.http2ClientConnectionUpdateSettings: Invalid aws_http_connection");
886 return;
887 }
888 if (!java_async_callback) {
889 aws_jni_throw_illegal_argument_exception(
890 env, "Http2ClientConnection.http2ClientConnectionUpdateSettings: Invalid async callback");
891 return;
892 }
893 struct aws_allocator *allocator = aws_jni_get_allocator();
894 struct aws_http2_callback_data *callback_data = s_new_http2_callback_data(env, allocator, java_async_callback);
895
896 /* We marshalled each setting to two long integers, the long list will be number of settings times two */
897 const size_t len = (*env)->GetArrayLength(env, java_marshalled_settings);
898 AWS_ASSERT(len % 2 == 0);
899 const size_t settings_len = len / 2;
900 struct aws_http2_setting *settings =
901 settings_len ? aws_mem_calloc(allocator, settings_len, sizeof(struct aws_http2_setting)) : NULL;
902 int success = false;
903 jlong *marshalled_settings = (*env)->GetLongArrayElements(env, java_marshalled_settings, NULL);
904 for (size_t i = 0; i < settings_len; i++) {
905 jlong id = marshalled_settings[i * 2];
906 settings[i].id = id;
907 jlong value = marshalled_settings[i * 2 + 1];
908 settings[i].value = (uint32_t)value;
909 }
910
911 if (aws_http2_connection_change_settings(
912 native_conn, settings, settings_len, s_on_settings_completed, callback_data)) {
913 aws_jni_throw_runtime_exception(
914 env, "Http2ClientConnection.http2ClientConnectionUpdateSettings: failed to change settings");
915 goto done;
916 }
917 success = true;
918 done:
919 aws_mem_release(allocator, settings);
920 (*env)->ReleaseLongArrayElements(env, java_marshalled_settings, (jlong *)marshalled_settings, JNI_ABORT);
921 if (!success) {
922 s_cleanup_http2_callback_data(callback_data, env);
923 }
924 return;
925 }
926
s_on_ping_completed(struct aws_http_connection * http2_connection,uint64_t round_trip_time_ns,int error_code,void * user_data)927 static void s_on_ping_completed(
928 struct aws_http_connection *http2_connection,
929 uint64_t round_trip_time_ns,
930 int error_code,
931 void *user_data) {
932 (void)http2_connection;
933 struct aws_http2_callback_data *callback_data = user_data;
934
935 /********** JNI ENV ACQUIRE **********/
936 JavaVM *jvm = callback_data->jvm;
937 JNIEnv *env = aws_jni_acquire_thread_env(jvm);
938 if (env == NULL) {
939 /* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */
940 return;
941 }
942
943 if (error_code) {
944 jobject crt_exception = aws_jni_new_crt_exception_from_error_code(env, error_code);
945 (*env)->CallVoidMethod(env, callback_data->async_callback, async_callback_properties.on_failure, crt_exception);
946 (*env)->DeleteLocalRef(env, crt_exception);
947 } else {
948 jobject java_round_trip_time_ns = (*env)->NewObject(
949 env, boxed_long_properties.long_class, boxed_long_properties.constructor, (jlong)round_trip_time_ns);
950 (*env)->CallVoidMethod(
951 env,
952 callback_data->async_callback,
953 async_callback_properties.on_success_with_object,
954 java_round_trip_time_ns);
955 (*env)->DeleteLocalRef(env, java_round_trip_time_ns);
956 }
957 AWS_FATAL_ASSERT(!aws_jni_check_and_clear_exception(env));
958 s_cleanup_http2_callback_data(callback_data, env);
959
960 aws_jni_release_thread_env(jvm, env);
961 /********** JNI ENV RELEASE **********/
962 }
963
Java_software_amazon_awssdk_crt_http_Http2ClientConnection_http2ClientConnectionSendPing(JNIEnv * env,jclass jni_class,jlong jni_connection,jobject java_async_callback,jbyteArray ping_data)964 JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_http_Http2ClientConnection_http2ClientConnectionSendPing(
965 JNIEnv *env,
966 jclass jni_class,
967 jlong jni_connection,
968 jobject java_async_callback,
969 jbyteArray ping_data) {
970
971 (void)jni_class;
972 aws_cache_jni_ids(env);
973
974 struct aws_http_connection_binding *connection_binding = (struct aws_http_connection_binding *)jni_connection;
975 struct aws_http_connection *native_conn = connection_binding->connection;
976
977 if (!native_conn) {
978 aws_jni_throw_null_pointer_exception(
979 env, "Http2ClientConnection.http2ClientConnectionSendPing: Invalid aws_http_connection");
980 return;
981 }
982 if (!java_async_callback) {
983 aws_jni_throw_illegal_argument_exception(
984 env, "Http2ClientConnection.http2ClientConnectionSendPing: Invalid async callback");
985 return;
986 }
987 bool success = false;
988 struct aws_allocator *allocator = aws_jni_get_allocator();
989 struct aws_byte_cursor *ping_cur_pointer = NULL;
990 struct aws_byte_cursor ping_cur;
991 AWS_ZERO_STRUCT(ping_cur);
992 struct aws_http2_callback_data *callback_data = s_new_http2_callback_data(env, allocator, java_async_callback);
993
994 if (ping_data) {
995 ping_cur = aws_jni_byte_cursor_from_jbyteArray_acquire(env, ping_data);
996 ping_cur_pointer = &ping_cur;
997 }
998 if (aws_http2_connection_ping(native_conn, ping_cur_pointer, s_on_ping_completed, callback_data)) {
999 aws_jni_throw_runtime_exception(env, "Failed to send ping");
1000 goto done;
1001 }
1002 success = true;
1003 done:
1004 if (ping_cur_pointer) {
1005 aws_jni_byte_cursor_from_jbyteArray_release(env, ping_data, ping_cur);
1006 }
1007 if (!success) {
1008 s_cleanup_http2_callback_data(callback_data, env);
1009 }
1010 return;
1011 }
1012
Java_software_amazon_awssdk_crt_http_Http2ClientConnection_http2ClientConnectionSendGoAway(JNIEnv * env,jclass jni_class,jlong jni_connection,jlong h2_error_code,jboolean allow_more_streams,jbyteArray debug_data)1013 JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_http_Http2ClientConnection_http2ClientConnectionSendGoAway(
1014 JNIEnv *env,
1015 jclass jni_class,
1016 jlong jni_connection,
1017 jlong h2_error_code,
1018 jboolean allow_more_streams,
1019 jbyteArray debug_data) {
1020
1021 (void)jni_class;
1022 aws_cache_jni_ids(env);
1023
1024 struct aws_http_connection_binding *connection_binding = (struct aws_http_connection_binding *)jni_connection;
1025 struct aws_http_connection *native_conn = connection_binding->connection;
1026 struct aws_byte_cursor *debug_cur_pointer = NULL;
1027 struct aws_byte_cursor debug_cur;
1028 AWS_ZERO_STRUCT(debug_cur);
1029
1030 if (!native_conn) {
1031 aws_jni_throw_runtime_exception(
1032 env, "Http2ClientConnection.http2ClientConnectionSendGoAway: Invalid aws_http_connection");
1033 return;
1034 }
1035 if (debug_data) {
1036 debug_cur = aws_jni_byte_cursor_from_jbyteArray_acquire(env, debug_data);
1037 debug_cur_pointer = &debug_cur;
1038 }
1039 aws_http2_connection_send_goaway(native_conn, (uint32_t)h2_error_code, allow_more_streams, debug_cur_pointer);
1040 if (debug_cur_pointer) {
1041 aws_jni_byte_cursor_from_jbyteArray_release(env, debug_data, debug_cur);
1042 }
1043 return;
1044 }
1045
1046 JNIEXPORT void JNICALL
Java_software_amazon_awssdk_crt_http_Http2ClientConnection_http2ClientConnectionUpdateConnectionWindow(JNIEnv * env,jclass jni_class,jlong jni_connection,jlong increment_size)1047 Java_software_amazon_awssdk_crt_http_Http2ClientConnection_http2ClientConnectionUpdateConnectionWindow(
1048 JNIEnv *env,
1049 jclass jni_class,
1050 jlong jni_connection,
1051 jlong increment_size) {
1052
1053 (void)jni_class;
1054 aws_cache_jni_ids(env);
1055
1056 struct aws_http_connection_binding *connection_binding = (struct aws_http_connection_binding *)jni_connection;
1057 struct aws_http_connection *native_conn = connection_binding->connection;
1058
1059 if (!native_conn) {
1060 aws_jni_throw_runtime_exception(
1061 env, "Http2ClientConnection.http2ClientConnectionUpdateConnectionWindow: Invalid aws_http_connection");
1062 return;
1063 }
1064 /* We did range check in Java already. */
1065 aws_http2_connection_update_window(native_conn, (uint32_t)increment_size);
1066 return;
1067 }
1068
1069 #if UINTPTR_MAX == 0xffffffff
1070 # if defined(_MSC_VER)
1071 # pragma warning(pop)
1072 # else
1073 # pragma GCC diagnostic pop
1074 # endif
1075 #endif
1076