xref: /aosp_15_r20/external/aws-crt-java/src/native/event_stream_rpc_server.c (revision 3c7ae9de214676c52d19f01067dc1a404272dc11)
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 <aws/event-stream/event_stream_rpc_server.h>
9 
10 #include <aws/common/string.h>
11 #include <aws/io/tls_channel_handler.h>
12 
13 #include "crt.h"
14 #include "event_stream_message.h"
15 #include "java_class_ids.h"
16 
17 #if defined(_MSC_VER)
18 #    pragma warning(disable : 4204) /* non-constant aggregate initializer */
19 #endif
20 
21 /* on 32-bit platforms, casting pointers to longs throws a warning we don't need */
22 #if UINTPTR_MAX == 0xffffffff
23 #    if defined(_MSC_VER)
24 #        pragma warning(push)
25 #        pragma warning(disable : 4305) /* 'type cast': truncation from 'jlong' to 'jni_tls_ctx_options *' */
26 #    else
27 #        pragma GCC diagnostic push
28 #        pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
29 #        pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
30 #    endif
31 #endif
32 
33 struct shutdown_callback_data {
34     JavaVM *jvm;
35     jweak java_server_listener;
36     jobject java_listener_handler;
37 };
38 
s_shutdown_callback_data_destroy(JNIEnv * env,struct shutdown_callback_data * callback_data)39 static void s_shutdown_callback_data_destroy(JNIEnv *env, struct shutdown_callback_data *callback_data) {
40 
41     if (!callback_data) {
42         return;
43     }
44 
45     if (callback_data->java_server_listener) {
46         (*env)->DeleteWeakGlobalRef(env, callback_data->java_server_listener);
47     }
48 
49     if (callback_data->java_listener_handler) {
50         (*env)->DeleteGlobalRef(env, callback_data->java_listener_handler);
51     }
52 
53     aws_mem_release(aws_jni_get_allocator(), callback_data);
54 }
55 
56 struct connection_callback_data {
57     JavaVM *jvm;
58     jobject java_server_connection;
59     jweak java_listener_handler;
60     jobject java_connection_handler;
61 };
62 
s_server_connection_data_destroy(JNIEnv * env,struct connection_callback_data * callback_data)63 static void s_server_connection_data_destroy(JNIEnv *env, struct connection_callback_data *callback_data) {
64     if (!callback_data) {
65         return;
66     }
67 
68     if (callback_data->java_listener_handler) {
69         (*env)->DeleteGlobalRef(env, callback_data->java_listener_handler);
70     }
71 
72     if (callback_data->java_server_connection) {
73         (*env)->DeleteGlobalRef(env, callback_data->java_server_connection);
74     }
75 
76     if (callback_data->java_connection_handler) {
77         (*env)->DeleteGlobalRef(env, callback_data->java_connection_handler);
78     }
79 
80     aws_mem_release(aws_jni_get_allocator(), callback_data);
81 }
82 
s_server_listener_shutdown_complete(struct aws_event_stream_rpc_server_listener * listener,void * user_data)83 static void s_server_listener_shutdown_complete(
84     struct aws_event_stream_rpc_server_listener *listener,
85     void *user_data) {
86     (void)listener;
87 
88     struct shutdown_callback_data *callback_data = user_data;
89 
90     /********** JNI ENV ACQUIRE **********/
91     JNIEnv *env = aws_jni_acquire_thread_env(callback_data->jvm);
92     if (env == NULL) {
93         /* If we can't get an environment, then the JVM is probably shutting down.  Don't crash. */
94         return;
95     }
96 
97     jobject java_server_listener = (*env)->NewLocalRef(env, callback_data->java_server_listener);
98     if (java_server_listener) {
99         (*env)->CallVoidMethod(env, java_server_listener, event_stream_server_listener_properties.onShutdownComplete);
100         aws_jni_check_and_clear_exception(env);
101         (*env)->DeleteLocalRef(env, java_server_listener);
102     }
103 
104     JavaVM *jvm = callback_data->jvm;
105     s_shutdown_callback_data_destroy(env, callback_data);
106     aws_jni_release_thread_env(jvm, env);
107     /********** JNI ENV RELEASE **********/
108 }
109 
110 struct continuation_callback_data {
111     JavaVM *jvm;
112     jobject java_continuation;
113     jobject java_continuation_handler;
114 };
115 
s_server_continuation_data_destroy(JNIEnv * env,struct continuation_callback_data * callback_data)116 static void s_server_continuation_data_destroy(JNIEnv *env, struct continuation_callback_data *callback_data) {
117     if (!callback_data) {
118         return;
119     }
120 
121     if (callback_data->java_continuation_handler) {
122         (*env)->DeleteGlobalRef(env, callback_data->java_continuation_handler);
123     }
124 
125     if (callback_data->java_continuation) {
126         (*env)->DeleteGlobalRef(env, callback_data->java_continuation);
127     }
128 
129     aws_mem_release(aws_jni_get_allocator(), callback_data);
130 }
131 
s_stream_continuation_fn(struct aws_event_stream_rpc_server_continuation_token * token,const struct aws_event_stream_rpc_message_args * message_args,void * user_data)132 static void s_stream_continuation_fn(
133     struct aws_event_stream_rpc_server_continuation_token *token,
134     const struct aws_event_stream_rpc_message_args *message_args,
135     void *user_data) {
136     (void)token;
137 
138     struct continuation_callback_data *callback_data = user_data;
139 
140     /********** JNI ENV ACQUIRE **********/
141     JNIEnv *env = aws_jni_acquire_thread_env(callback_data->jvm);
142     if (env == NULL) {
143         /* If we can't get an environment, then the JVM is probably shutting down.  Don't crash. */
144         return;
145     }
146 
147     jbyteArray headers_array = aws_event_stream_rpc_marshall_headers_to_byteArray(
148         aws_jni_get_allocator(), env, message_args->headers, message_args->headers_count);
149 
150     struct aws_byte_cursor payload_cur = aws_byte_cursor_from_buf(message_args->payload);
151     jbyteArray payload_byte_array = aws_jni_byte_array_from_cursor(env, &payload_cur);
152     (*env)->CallVoidMethod(
153         env,
154         callback_data->java_continuation_handler,
155         event_stream_server_continuation_handler_properties.onContinuationMessage,
156         headers_array,
157         payload_byte_array,
158         (jint)message_args->message_type,
159         (jint)message_args->message_flags);
160     (*env)->DeleteLocalRef(env, headers_array);
161     (*env)->DeleteLocalRef(env, payload_byte_array);
162     /* don't really care if they threw here, but we want to make the jvm happy that we checked */
163     aws_jni_check_and_clear_exception(env);
164 
165     aws_jni_release_thread_env(callback_data->jvm, env);
166     /********** JNI ENV RELEASE **********/
167 }
168 
s_stream_continuation_closed_fn(struct aws_event_stream_rpc_server_continuation_token * token,void * user_data)169 static void s_stream_continuation_closed_fn(
170     struct aws_event_stream_rpc_server_continuation_token *token,
171     void *user_data) {
172     (void)token;
173     struct continuation_callback_data *continuation_callback_data = user_data;
174 
175     /********** JNI ENV ACQUIRE **********/
176     JNIEnv *env = aws_jni_acquire_thread_env(continuation_callback_data->jvm);
177     if (env == NULL) {
178         /* If we can't get an environment, then the JVM is probably shutting down.  Don't crash. */
179         return;
180     }
181 
182     (*env)->CallVoidMethod(
183         env,
184         continuation_callback_data->java_continuation_handler,
185         event_stream_server_continuation_handler_properties.onContinuationClosed);
186     /* don't really care if they threw here, but we want to make the jvm happy that we checked */
187     aws_jni_check_and_clear_exception(env);
188 
189     JavaVM *jvm = continuation_callback_data->jvm;
190     s_server_continuation_data_destroy(env, continuation_callback_data);
191     aws_jni_release_thread_env(jvm, env);
192     /********** JNI ENV RELEASE **********/
193 }
194 
s_on_incoming_stream_fn(struct aws_event_stream_rpc_server_connection * connection,struct aws_event_stream_rpc_server_continuation_token * token,struct aws_byte_cursor operation_name,struct aws_event_stream_rpc_server_stream_continuation_options * continuation_options,void * user_data)195 static int s_on_incoming_stream_fn(
196     struct aws_event_stream_rpc_server_connection *connection,
197     struct aws_event_stream_rpc_server_continuation_token *token,
198     struct aws_byte_cursor operation_name,
199     struct aws_event_stream_rpc_server_stream_continuation_options *continuation_options,
200     void *user_data) {
201     (void)connection;
202 
203     struct connection_callback_data *callback_data = user_data;
204 
205     jobject java_continuation = NULL;
206     jobject java_continuation_handler = NULL;
207 
208     /********** JNI ENV ACQUIRE **********/
209     JNIEnv *env = aws_jni_acquire_thread_env(callback_data->jvm);
210     if (env == NULL) {
211         /* If we can't get an environment, then the JVM is probably shutting down.  Don't crash. */
212         return AWS_OP_ERR;
213     }
214 
215     struct continuation_callback_data *continuation_callback_data =
216         aws_mem_calloc(aws_jni_get_allocator(), 1, sizeof(struct continuation_callback_data));
217 
218     if (!continuation_callback_data) {
219         goto on_error;
220     }
221 
222     continuation_callback_data->jvm = callback_data->jvm;
223 
224     java_continuation = (*env)->NewObject(
225         env,
226         event_stream_server_connection_handler_properties.continuationCls,
227         event_stream_server_connection_handler_properties.newContinuationConstructor,
228         (jlong)token);
229 
230     aws_jni_check_and_clear_exception(env);
231 
232     if (!java_continuation) {
233         aws_raise_error(AWS_ERROR_INVALID_STATE);
234         goto on_error;
235     }
236 
237     continuation_callback_data->java_continuation = (*env)->NewGlobalRef(env, java_continuation);
238     if (continuation_callback_data->java_continuation == NULL) {
239         aws_raise_error(AWS_ERROR_INVALID_STATE);
240         goto on_error;
241     }
242 
243     java_continuation_handler = NULL;
244     jbyteArray operation_name_array = aws_jni_byte_array_from_cursor(env, &operation_name);
245     if (operation_name_array != NULL) {
246         java_continuation_handler = (*env)->CallObjectMethod(
247             env,
248             callback_data->java_connection_handler,
249             event_stream_server_connection_handler_properties.onIncomingStream,
250             java_continuation,
251             operation_name_array);
252         (*env)->DeleteLocalRef(env, operation_name_array);
253         aws_jni_check_and_clear_exception(env);
254     }
255 
256     if (!java_continuation_handler) {
257         aws_raise_error(AWS_ERROR_INVALID_STATE);
258         goto on_error;
259     }
260 
261     continuation_callback_data->java_continuation_handler = (*env)->NewGlobalRef(env, java_continuation_handler);
262     if (continuation_callback_data->java_continuation_handler == NULL) {
263         aws_raise_error(AWS_ERROR_INVALID_STATE);
264         goto on_error;
265     }
266 
267     continuation_options->user_data = continuation_callback_data;
268     continuation_options->on_continuation = s_stream_continuation_fn;
269     continuation_options->on_continuation_closed = s_stream_continuation_closed_fn;
270 
271     (*env)->DeleteLocalRef(env, java_continuation_handler);
272     (*env)->DeleteLocalRef(env, java_continuation);
273 
274     aws_jni_release_thread_env(callback_data->jvm, env);
275     /********** JNI ENV RELEASE SUCCESS PATH **********/
276 
277     return AWS_OP_SUCCESS;
278 
279 on_error:
280 
281     if (java_continuation_handler != NULL) {
282         (*env)->DeleteLocalRef(env, java_continuation_handler);
283     }
284 
285     if (java_continuation != NULL) {
286         (*env)->DeleteLocalRef(env, java_continuation);
287     }
288 
289     aws_event_stream_rpc_server_connection_close(connection, aws_last_error());
290     s_server_continuation_data_destroy(env, continuation_callback_data);
291 
292     aws_jni_release_thread_env(callback_data->jvm, env);
293     /********** JNI ENV RELEASE FAILURE PATH **********/
294 
295     return AWS_OP_ERR;
296 }
297 
s_connection_protocol_message_fn(struct aws_event_stream_rpc_server_connection * connection,const struct aws_event_stream_rpc_message_args * message_args,void * user_data)298 static void s_connection_protocol_message_fn(
299     struct aws_event_stream_rpc_server_connection *connection,
300     const struct aws_event_stream_rpc_message_args *message_args,
301     void *user_data) {
302     (void)connection;
303 
304     struct connection_callback_data *callback_data = user_data;
305 
306     /********** JNI ENV ACQUIRE **********/
307     JNIEnv *env = aws_jni_acquire_thread_env(callback_data->jvm);
308     if (env == NULL) {
309         /* If we can't get an environment, then the JVM is probably shutting down.  Don't crash. */
310         return;
311     }
312 
313     jbyteArray headers_array = aws_event_stream_rpc_marshall_headers_to_byteArray(
314         aws_jni_get_allocator(), env, message_args->headers, message_args->headers_count);
315 
316     struct aws_byte_cursor payload_cur = aws_byte_cursor_from_buf(message_args->payload);
317     jbyteArray payload_byte_array = aws_jni_byte_array_from_cursor(env, &payload_cur);
318 
319     (*env)->CallVoidMethod(
320         env,
321         callback_data->java_connection_handler,
322         event_stream_server_connection_handler_properties.onProtocolMessage,
323         headers_array,
324         payload_byte_array,
325         (jint)message_args->message_type,
326         (jint)message_args->message_flags);
327     (*env)->DeleteLocalRef(env, headers_array);
328     (*env)->DeleteLocalRef(env, payload_byte_array);
329     /* don't really care if they threw here, but we want to make the jvm happy that we checked */
330     aws_jni_check_and_clear_exception(env);
331 
332     aws_jni_release_thread_env(callback_data->jvm, env);
333     /********** JNI ENV RELEASE **********/
334 }
335 
s_on_new_connection_fn(struct aws_event_stream_rpc_server_connection * connection,int error_code,struct aws_event_stream_rpc_connection_options * connection_options,void * user_data)336 static int s_on_new_connection_fn(
337     struct aws_event_stream_rpc_server_connection *connection,
338     int error_code,
339     struct aws_event_stream_rpc_connection_options *connection_options,
340     void *user_data) {
341 
342     struct shutdown_callback_data *callback_data = user_data;
343 
344     /********** JNI ENV ACQUIRE **********/
345     JNIEnv *env = aws_jni_acquire_thread_env(callback_data->jvm);
346     if (env == NULL) {
347         /* If we can't get an environment, then the JVM is probably shutting down.  Don't crash. */
348         return AWS_OP_ERR;
349     }
350 
351     jobject java_server_connection = NULL;
352     jobject java_connection_handler = NULL;
353 
354     struct connection_callback_data *connection_callback_data =
355         aws_mem_calloc(aws_jni_get_allocator(), 1, sizeof(struct connection_callback_data));
356 
357     if (!connection_callback_data) {
358         goto error;
359     }
360 
361     connection_callback_data->jvm = callback_data->jvm;
362     connection_callback_data->java_listener_handler = (*env)->NewGlobalRef(env, callback_data->java_listener_handler);
363 
364     if (!error_code) {
365         java_server_connection = (*env)->NewObject(
366             env,
367             event_stream_server_listener_handler_properties.connCls,
368             event_stream_server_listener_handler_properties.newConnConstructor,
369             (jlong)connection);
370         if (aws_jni_check_and_clear_exception(env) || java_server_connection == NULL) {
371             aws_raise_error(AWS_ERROR_INVALID_STATE);
372             goto error;
373         }
374 
375         connection_callback_data->java_server_connection = (*env)->NewGlobalRef(env, java_server_connection);
376     }
377 
378     java_connection_handler = (*env)->CallObjectMethod(
379         env,
380         connection_callback_data->java_listener_handler,
381         event_stream_server_listener_handler_properties.onNewConnection,
382         java_server_connection,
383         error_code);
384 
385     /* we check whether the function succeeded when we do the null check on java_connection_handler below */
386     aws_jni_check_and_clear_exception(env);
387 
388     if (!java_connection_handler) {
389         aws_raise_error(AWS_ERROR_INVALID_STATE);
390         goto error;
391     }
392 
393     connection_callback_data->java_connection_handler = (*env)->NewGlobalRef(env, java_connection_handler);
394     if (connection_callback_data->java_connection_handler == NULL) {
395         aws_raise_error(AWS_ERROR_INVALID_STATE);
396         goto error;
397     }
398 
399     /* Nothing can fail after here */
400     connection_options->on_connection_protocol_message = s_connection_protocol_message_fn;
401     connection_options->on_incoming_stream = s_on_incoming_stream_fn;
402     connection_options->user_data = connection_callback_data;
403 
404     (*env)->DeleteLocalRef(env, java_connection_handler);
405     (*env)->DeleteLocalRef(env, java_server_connection);
406 
407     aws_jni_release_thread_env(callback_data->jvm, env);
408     /********** JNI ENV RELEASE SUCCESS PATH **********/
409 
410     return AWS_OP_SUCCESS;
411 
412 error:
413 
414     if (java_connection_handler != NULL) {
415         (*env)->DeleteLocalRef(env, java_connection_handler);
416     }
417 
418     if (java_server_connection != NULL) {
419         (*env)->DeleteLocalRef(env, java_server_connection);
420     }
421 
422     s_server_connection_data_destroy(env, connection_callback_data);
423 
424     aws_jni_release_thread_env(callback_data->jvm, env);
425     /********** JNI ENV RELEASE ERROR PATH **********/
426 
427     return AWS_OP_ERR;
428 }
429 
s_on_connection_shutdown_fn(struct aws_event_stream_rpc_server_connection * connection,int error_code,void * user_data)430 static void s_on_connection_shutdown_fn(
431     struct aws_event_stream_rpc_server_connection *connection,
432     int error_code,
433     void *user_data) {
434 
435     (void)user_data;
436     struct connection_callback_data *callback_data = aws_event_stream_rpc_server_connection_get_user_data(connection);
437     if (!callback_data) {
438         /* The connection was not setup correctly. Probably failed within s_on_new_connection_fn. Early out. */
439         return;
440     }
441 
442     /********** JNI ENV ACQUIRE **********/
443     JNIEnv *env = aws_jni_acquire_thread_env(callback_data->jvm);
444     if (env == NULL) {
445         /* If we can't get an environment, then the JVM is probably shutting down.  Don't crash. */
446         return;
447     }
448 
449     jobject java_listener_handler = (*env)->NewLocalRef(env, callback_data->java_listener_handler);
450     jobject java_server_connection = (*env)->NewLocalRef(env, callback_data->java_server_connection);
451 
452     AWS_FATAL_ASSERT(java_listener_handler);
453     AWS_FATAL_ASSERT(java_server_connection);
454 
455     /* Tell the Java ListenerHandler that the connection shutdown. */
456     (*env)->CallVoidMethod(
457         env,
458         java_listener_handler,
459         event_stream_server_listener_handler_properties.onConnectionShutdown,
460         java_server_connection,
461         error_code);
462     aws_jni_check_and_clear_exception(env);
463 
464     (*env)->DeleteLocalRef(env, java_server_connection);
465     (*env)->DeleteLocalRef(env, java_listener_handler);
466 
467     /* this is the should be connection specific callback data. */
468     JavaVM *jvm = callback_data->jvm;
469     s_server_connection_data_destroy(env, callback_data);
470     aws_jni_release_thread_env(jvm, env);
471     /********** JNI ENV RELEASE **********/
472 }
473 
474 JNIEXPORT
Java_software_amazon_awssdk_crt_eventstream_ServerListener_serverListenerNew(JNIEnv * env,jclass jni_class,jobject jni_server_listener,jbyteArray jni_host_name,jint port,jlong jni_socket_options,jlong jni_tls_ctx,jlong jni_server_bootstrap,jobject jni_server_listener_handler)475 jlong JNICALL Java_software_amazon_awssdk_crt_eventstream_ServerListener_serverListenerNew(
476     JNIEnv *env,
477     jclass jni_class,
478     jobject jni_server_listener,
479     jbyteArray jni_host_name,
480     jint port,
481     jlong jni_socket_options,
482     jlong jni_tls_ctx,
483     jlong jni_server_bootstrap,
484     jobject jni_server_listener_handler) {
485     (void)jni_class;
486     aws_cache_jni_ids(env);
487 
488     struct aws_server_bootstrap *server_bootstrap = (struct aws_server_bootstrap *)jni_server_bootstrap;
489     struct aws_socket_options *socket_options = (struct aws_socket_options *)jni_socket_options;
490     struct aws_tls_ctx *tls_context = (struct aws_tls_ctx *)jni_tls_ctx;
491 
492     if (!server_bootstrap) {
493         aws_jni_throw_runtime_exception(env, "ServerListener.server_listener_new: Invalid ServerBootstrap");
494         return (jlong)NULL;
495     }
496 
497     if (!socket_options) {
498         aws_jni_throw_runtime_exception(env, "ServerListener.server_listener_new: Invalid SocketOptions");
499         return (jlong)NULL;
500     }
501 
502     struct aws_tls_connection_options connection_options;
503     AWS_ZERO_STRUCT(connection_options);
504     struct aws_tls_connection_options *conn_options_ptr = NULL;
505     struct aws_string *host_name_str = NULL;
506 
507     if (tls_context) {
508         aws_tls_connection_options_init_from_ctx(&connection_options, tls_context);
509         conn_options_ptr = &connection_options;
510     }
511 
512     struct aws_allocator *allocator = aws_jni_get_allocator();
513 
514     struct shutdown_callback_data *callback_data = aws_mem_calloc(allocator, 1, sizeof(struct shutdown_callback_data));
515     if (!callback_data) {
516         aws_jni_throw_runtime_exception(env, "ServerListener.server_listener_new: Unable to allocate");
517         goto error;
518     }
519 
520     jint jvmresult = (*env)->GetJavaVM(env, &callback_data->jvm);
521     if (jvmresult != 0) {
522         aws_jni_throw_runtime_exception(env, "ServerListener.server_listener_new: Unable to get JVM");
523         goto error;
524     }
525 
526     callback_data->java_server_listener = (*env)->NewWeakGlobalRef(env, jni_server_listener);
527     if (!callback_data->java_server_listener) {
528         aws_jni_throw_runtime_exception(env, "ServerListener.server_listener_new: Unable to create global weak ref");
529         goto error;
530     }
531 
532     callback_data->java_listener_handler = (*env)->NewGlobalRef(env, jni_server_listener_handler);
533     if (!callback_data->java_listener_handler) {
534         aws_jni_throw_runtime_exception(env, "ServerListener.server_listener_new: Unable to create global ref");
535         goto error;
536     }
537 
538     const size_t host_name_len = (*env)->GetArrayLength(env, jni_host_name);
539     jbyte *host_name = (*env)->GetPrimitiveArrayCritical(env, jni_host_name, NULL);
540     host_name_str = aws_string_new_from_array(allocator, (uint8_t *)host_name, host_name_len);
541     (*env)->ReleasePrimitiveArrayCritical(env, jni_host_name, host_name, 0);
542 
543     if (!host_name_str) {
544         aws_jni_throw_runtime_exception(env, "ServerListener.server_listener_new: Unable to allocate");
545         goto error;
546     }
547 
548     const char *c_str_host_name = aws_string_c_str(host_name_str);
549 
550     struct aws_event_stream_rpc_server_listener_options listener_options = {
551         .socket_options = socket_options,
552         .on_destroy_callback = s_server_listener_shutdown_complete,
553         .bootstrap = server_bootstrap,
554         .tls_options = conn_options_ptr,
555         .port = (uint32_t)port,
556         .host_name = c_str_host_name,
557         .user_data = callback_data,
558         .on_new_connection = s_on_new_connection_fn,
559         .on_connection_shutdown = s_on_connection_shutdown_fn,
560     };
561 
562     struct aws_event_stream_rpc_server_listener *listener =
563         aws_event_stream_rpc_server_new_listener(allocator, &listener_options);
564     aws_string_destroy(host_name_str);
565     host_name_str = NULL;
566 
567     if (!listener) {
568         aws_jni_throw_runtime_exception(
569             env, "ServerBootstrap.server_bootstrap_new: Unable to allocate new aws_event_stream_rpc_server_listener");
570         goto error;
571     }
572 
573     return (jlong)listener;
574 
575 error:
576     if (host_name_str) {
577         aws_string_destroy(host_name_str);
578     }
579 
580     if (conn_options_ptr) {
581         aws_tls_connection_options_clean_up(conn_options_ptr);
582     }
583 
584     s_shutdown_callback_data_destroy(env, callback_data);
585     return (jlong)NULL;
586 }
587 
588 JNIEXPORT
Java_software_amazon_awssdk_crt_eventstream_ServerListener_getBoundPort(JNIEnv * env,jclass jni_class,jlong jni_server_listener)589 jint JNICALL Java_software_amazon_awssdk_crt_eventstream_ServerListener_getBoundPort(
590     JNIEnv *env,
591     jclass jni_class,
592     jlong jni_server_listener) {
593     (void)env;
594     (void)jni_class;
595     aws_cache_jni_ids(env);
596 
597     struct aws_event_stream_rpc_server_listener *listener =
598         (struct aws_event_stream_rpc_server_listener *)jni_server_listener;
599     if (!listener) {
600         aws_jni_throw_runtime_exception(env, "ServerListener.getBoundPort: Invalid serverListener");
601         return -1;
602     }
603 
604     return (jint)aws_event_stream_rpc_server_listener_get_bound_port(listener);
605 }
606 
607 JNIEXPORT
Java_software_amazon_awssdk_crt_eventstream_ServerListener_release(JNIEnv * env,jclass jni_class,jlong jni_server_listener)608 void JNICALL Java_software_amazon_awssdk_crt_eventstream_ServerListener_release(
609     JNIEnv *env,
610     jclass jni_class,
611     jlong jni_server_listener) {
612     (void)env;
613     (void)jni_class;
614     aws_cache_jni_ids(env);
615 
616     struct aws_event_stream_rpc_server_listener *listener =
617         (struct aws_event_stream_rpc_server_listener *)jni_server_listener;
618     if (!listener) {
619         return;
620     }
621 
622     aws_event_stream_rpc_server_listener_release(listener);
623 }
624 
625 JNIEXPORT
Java_software_amazon_awssdk_crt_eventstream_ServerConnection_acquire(JNIEnv * env,jclass jni_class,jlong jni_server_connection)626 void JNICALL Java_software_amazon_awssdk_crt_eventstream_ServerConnection_acquire(
627     JNIEnv *env,
628     jclass jni_class,
629     jlong jni_server_connection) {
630     (void)env;
631     (void)jni_class;
632     aws_cache_jni_ids(env);
633 
634     struct aws_event_stream_rpc_server_connection *connection =
635         (struct aws_event_stream_rpc_server_connection *)jni_server_connection;
636     if (connection == NULL) {
637         return;
638     }
639 
640     aws_event_stream_rpc_server_connection_acquire(connection);
641 }
642 
643 JNIEXPORT
Java_software_amazon_awssdk_crt_eventstream_ServerConnection_release(JNIEnv * env,jclass jni_class,jlong jni_server_connection)644 void JNICALL Java_software_amazon_awssdk_crt_eventstream_ServerConnection_release(
645     JNIEnv *env,
646     jclass jni_class,
647     jlong jni_server_connection) {
648     (void)env;
649     (void)jni_class;
650     aws_cache_jni_ids(env);
651 
652     struct aws_event_stream_rpc_server_connection *connection =
653         (struct aws_event_stream_rpc_server_connection *)jni_server_connection;
654     if (connection == NULL) {
655         return;
656     }
657 
658     aws_event_stream_rpc_server_connection_release(connection);
659 }
660 
661 JNIEXPORT
Java_software_amazon_awssdk_crt_eventstream_ServerConnection_closeConnection(JNIEnv * env,jclass jni_class,jlong jni_server_connection,jint error_code)662 void JNICALL Java_software_amazon_awssdk_crt_eventstream_ServerConnection_closeConnection(
663     JNIEnv *env,
664     jclass jni_class,
665     jlong jni_server_connection,
666     jint error_code) {
667     (void)env;
668     (void)jni_class;
669     aws_cache_jni_ids(env);
670 
671     struct aws_event_stream_rpc_server_connection *connection =
672         (struct aws_event_stream_rpc_server_connection *)jni_server_connection;
673     if (connection == NULL) {
674         return;
675     }
676 
677     aws_event_stream_rpc_server_connection_close(connection, error_code);
678 }
679 
680 JNIEXPORT
Java_software_amazon_awssdk_crt_eventstream_ServerConnection_isOpen(JNIEnv * env,jclass jni_class,jlong jni_server_connection)681 jboolean JNICALL Java_software_amazon_awssdk_crt_eventstream_ServerConnection_isOpen(
682     JNIEnv *env,
683     jclass jni_class,
684     jlong jni_server_connection) {
685     (void)env;
686     (void)jni_class;
687     aws_cache_jni_ids(env);
688 
689     struct aws_event_stream_rpc_server_connection *connection =
690         (struct aws_event_stream_rpc_server_connection *)jni_server_connection;
691 
692     if (connection == NULL) {
693         return false;
694     }
695 
696     return aws_event_stream_rpc_server_connection_is_open(connection);
697 }
698 
699 struct message_flush_callback_args {
700     JavaVM *jvm;
701     jobject callback;
702 };
703 
s_destroy_message_flush_callback_args(JNIEnv * env,struct message_flush_callback_args * callback_args)704 static void s_destroy_message_flush_callback_args(JNIEnv *env, struct message_flush_callback_args *callback_args) {
705     if (callback_args == NULL) {
706         return;
707     }
708 
709     if (callback_args->callback != NULL) {
710         (*env)->DeleteGlobalRef(env, callback_args->callback);
711     }
712 
713     aws_mem_release(aws_jni_get_allocator(), callback_args);
714 }
715 
s_message_flush_fn(int error_code,void * user_data)716 static void s_message_flush_fn(int error_code, void *user_data) {
717     struct message_flush_callback_args *callback_data = user_data;
718 
719     /********** JNI ENV ACQUIRE **********/
720     JNIEnv *env = aws_jni_acquire_thread_env(callback_data->jvm);
721     if (env == NULL) {
722         /* If we can't get an environment, then the JVM is probably shutting down.  Don't crash. */
723         return;
724     }
725 
726     (*env)->CallVoidMethod(
727         env, callback_data->callback, event_stream_server_message_flush_properties.callback, error_code);
728     aws_jni_check_and_clear_exception(env);
729 
730     JavaVM *jvm = callback_data->jvm;
731     s_destroy_message_flush_callback_args(env, callback_data);
732     aws_jni_release_thread_env(jvm, env);
733     /********** JNI ENV RELEASE **********/
734 }
735 
736 JNIEXPORT
Java_software_amazon_awssdk_crt_eventstream_ServerConnection_sendProtocolMessage(JNIEnv * env,jclass jni_class,jlong jni_server_connection,jbyteArray headers,jbyteArray payload,jint message_type,jint message_flags,jobject callback)737 jint JNICALL Java_software_amazon_awssdk_crt_eventstream_ServerConnection_sendProtocolMessage(
738     JNIEnv *env,
739     jclass jni_class,
740     jlong jni_server_connection,
741     jbyteArray headers,
742     jbyteArray payload,
743     jint message_type,
744     jint message_flags,
745     jobject callback) {
746     (void)jni_class;
747     aws_cache_jni_ids(env);
748 
749     struct aws_event_stream_rpc_server_connection *connection =
750         (struct aws_event_stream_rpc_server_connection *)jni_server_connection;
751 
752     int ret_val = AWS_OP_ERR;
753     struct message_flush_callback_args *callback_data = NULL;
754 
755     struct aws_event_stream_rpc_marshalled_message marshalled_message;
756     if (aws_event_stream_rpc_marshall_message_args_init(
757             &marshalled_message, aws_jni_get_allocator(), env, headers, payload, NULL, message_flags, message_type)) {
758         goto clean_up;
759     }
760 
761     if (connection == NULL) {
762         aws_jni_throw_runtime_exception(env, "ServerConnection.sendProtocolMessage: native connection is NULL.");
763         goto clean_up;
764     }
765 
766     callback_data = aws_mem_calloc(aws_jni_get_allocator(), 1, sizeof(struct message_flush_callback_args));
767 
768     if (!callback_data) {
769         aws_jni_throw_runtime_exception(env, "ServerConnection.sendProtocolMessage: allocation failed.");
770         goto clean_up;
771     }
772 
773     jint jvmresult = (*env)->GetJavaVM(env, &callback_data->jvm);
774     if (jvmresult != 0) {
775         aws_jni_throw_runtime_exception(env, "ServerConnection.sendProtocolMessage: Unable to get JVM");
776         goto clean_up;
777     }
778 
779     callback_data->callback = (*env)->NewGlobalRef(env, callback);
780     if (callback_data->callback == NULL) {
781         aws_jni_throw_runtime_exception(
782             env, "ServerConnection.sendProtocolMessage: Unable to create global ref to callback");
783         goto clean_up;
784     }
785 
786     if (aws_event_stream_rpc_server_connection_send_protocol_message(
787             connection, &marshalled_message.message_args, s_message_flush_fn, callback_data)) {
788         aws_jni_throw_runtime_exception(env, "ServerConnection.sendProtocolMessage: send message failed");
789         goto clean_up;
790     }
791 
792     ret_val = AWS_OP_SUCCESS;
793 
794 clean_up:
795 
796     aws_event_stream_rpc_marshall_message_args_clean_up(&marshalled_message);
797     if (ret_val != AWS_OP_SUCCESS) {
798         s_destroy_message_flush_callback_args(env, callback_data);
799     }
800 
801     return ret_val;
802 }
803 
804 JNIEXPORT
Java_software_amazon_awssdk_crt_eventstream_ServerConnectionContinuation_acquire(JNIEnv * env,jclass jni_class,jlong jni_server_continuation)805 void JNICALL Java_software_amazon_awssdk_crt_eventstream_ServerConnectionContinuation_acquire(
806     JNIEnv *env,
807     jclass jni_class,
808     jlong jni_server_continuation) {
809     (void)env;
810     (void)jni_class;
811     aws_cache_jni_ids(env);
812 
813     struct aws_event_stream_rpc_server_continuation_token *continuation =
814         (struct aws_event_stream_rpc_server_continuation_token *)jni_server_continuation;
815     aws_event_stream_rpc_server_continuation_acquire(continuation);
816 }
817 
818 JNIEXPORT
Java_software_amazon_awssdk_crt_eventstream_ServerConnectionContinuation_release(JNIEnv * env,jclass jni_class,jlong jni_server_continuation)819 void JNICALL Java_software_amazon_awssdk_crt_eventstream_ServerConnectionContinuation_release(
820     JNIEnv *env,
821     jclass jni_class,
822     jlong jni_server_continuation) {
823     (void)env;
824     (void)jni_class;
825     aws_cache_jni_ids(env);
826 
827     struct aws_event_stream_rpc_server_continuation_token *continuation =
828         (struct aws_event_stream_rpc_server_continuation_token *)jni_server_continuation;
829     aws_event_stream_rpc_server_continuation_release(continuation);
830 }
831 
832 JNIEXPORT
Java_software_amazon_awssdk_crt_eventstream_ServerConnectionContinuation_isClosed(JNIEnv * env,jclass jni_class,jlong jni_server_continuation)833 jboolean JNICALL Java_software_amazon_awssdk_crt_eventstream_ServerConnectionContinuation_isClosed(
834     JNIEnv *env,
835     jclass jni_class,
836     jlong jni_server_continuation) {
837     (void)env;
838     (void)jni_class;
839     aws_cache_jni_ids(env);
840 
841     struct aws_event_stream_rpc_server_continuation_token *continuation =
842         (struct aws_event_stream_rpc_server_continuation_token *)jni_server_continuation;
843 
844     if (continuation == NULL) {
845         return true;
846     }
847 
848     return aws_event_stream_rpc_server_continuation_is_closed(continuation);
849 }
850 
851 JNIEXPORT
Java_software_amazon_awssdk_crt_eventstream_ServerConnectionContinuation_sendContinuationMessage(JNIEnv * env,jclass jni_class,jlong jni_server_continuation,jbyteArray headers,jbyteArray payload,jint message_type,jint message_flags,jobject callback)852 jint JNICALL Java_software_amazon_awssdk_crt_eventstream_ServerConnectionContinuation_sendContinuationMessage(
853     JNIEnv *env,
854     jclass jni_class,
855     jlong jni_server_continuation,
856     jbyteArray headers,
857     jbyteArray payload,
858     jint message_type,
859     jint message_flags,
860     jobject callback) {
861     (void)jni_class;
862     aws_cache_jni_ids(env);
863 
864     struct aws_event_stream_rpc_server_continuation_token *continuation =
865         (struct aws_event_stream_rpc_server_continuation_token *)jni_server_continuation;
866 
867     struct message_flush_callback_args *callback_data = NULL;
868 
869     int ret_val = AWS_OP_ERR;
870 
871     struct aws_event_stream_rpc_marshalled_message marshalled_message;
872     if (aws_event_stream_rpc_marshall_message_args_init(
873             &marshalled_message, aws_jni_get_allocator(), env, headers, payload, NULL, message_flags, message_type)) {
874         goto clean_up;
875     }
876 
877     if (continuation == NULL) {
878         aws_jni_throw_runtime_exception(env, "ServerConnection.sendContinuationMessage: native continuation is NULL.");
879         goto clean_up;
880     }
881 
882     callback_data = aws_mem_calloc(aws_jni_get_allocator(), 1, sizeof(struct message_flush_callback_args));
883     if (!callback_data) {
884         aws_jni_throw_runtime_exception(
885             env, "ServerConnectionContinuation.sendContinuationMessage: allocation failed.");
886         goto clean_up;
887     }
888 
889     jint jvmresult = (*env)->GetJavaVM(env, &callback_data->jvm);
890     if (jvmresult != 0) {
891         aws_jni_throw_runtime_exception(env, "ServerConnectionContinuation.sendContinuationMessage: Unable to get JVM");
892         goto clean_up;
893     }
894 
895     callback_data->callback = (*env)->NewGlobalRef(env, callback);
896     if (callback_data->callback == NULL) {
897         aws_jni_throw_runtime_exception(
898             env, "ServerConnection.sendContinuationMessage: Unable to create global ref to callback");
899         goto clean_up;
900     }
901 
902     if (aws_event_stream_rpc_server_continuation_send_message(
903             continuation, &marshalled_message.message_args, s_message_flush_fn, callback_data)) {
904         aws_jni_throw_runtime_exception(
905             env, "ServerConnectionContinuation.sendContinuationMessage: send message failed");
906         goto clean_up;
907     }
908 
909     ret_val = AWS_OP_SUCCESS;
910 
911 clean_up:
912     aws_event_stream_rpc_marshall_message_args_clean_up(&marshalled_message);
913     if (ret_val != AWS_OP_SUCCESS) {
914         s_destroy_message_flush_callback_args(env, callback_data);
915     }
916 
917     return ret_val;
918 }
919 
920 #if UINTPTR_MAX == 0xffffffff
921 #    if defined(_MSC_VER)
922 #        pragma warning(pop)
923 #    else
924 #        pragma GCC diagnostic pop
925 #    endif
926 #endif
927