xref: /aosp_15_r20/external/aws-crt-java/src/native/event_stream_message.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.h>
9 
10 #include "crt.h"
11 #include "event_stream_message.h"
12 #include "java_class_ids.h"
13 
14 /* on 32-bit platforms, casting pointers to longs throws a warning we don't need */
15 #if UINTPTR_MAX == 0xffffffff
16 #    if defined(_MSC_VER)
17 #        pragma warning(push)
18 #        pragma warning(disable : 4305) /* 'type cast': truncation from 'jlong' to 'jni_tls_ctx_options *' */
19 #    else
20 #        pragma GCC diagnostic push
21 #        pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
22 #        pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
23 #    endif
24 #endif
25 
26 JNIEXPORT
Java_software_amazon_awssdk_crt_eventstream_Message_messageNew(JNIEnv * env,jclass jni_class,jbyteArray headers,jbyteArray payload)27 jlong JNICALL Java_software_amazon_awssdk_crt_eventstream_Message_messageNew(
28     JNIEnv *env,
29     jclass jni_class,
30     jbyteArray headers,
31     jbyteArray payload) {
32     (void)jni_class;
33     aws_cache_jni_ids(env);
34 
35     struct aws_event_stream_message *message =
36         aws_mem_calloc(aws_jni_get_allocator(), 1, sizeof(struct aws_event_stream_message));
37 
38     if (!message) {
39         aws_jni_throw_runtime_exception(env, "Message.MessageNew: Allocation failed!");
40         return (jlong)NULL;
41     }
42 
43     struct aws_event_stream_message *return_message = NULL;
44 
45     struct aws_event_stream_rpc_marshalled_message marshalled_message;
46     if (aws_event_stream_rpc_marshall_message_args_init(
47             &marshalled_message, aws_jni_get_allocator(), env, headers, payload, NULL, 0, 0)) {
48         goto clean_up;
49     }
50 
51     if (aws_event_stream_message_init(
52             message, aws_jni_get_allocator(), &marshalled_message.headers_list, &marshalled_message.payload_buf)) {
53         goto clean_up;
54     }
55 
56     return_message = message;
57 
58 clean_up:
59     aws_event_stream_rpc_marshall_message_args_clean_up(&marshalled_message);
60 
61     if (!return_message) {
62         aws_mem_release(aws_jni_get_allocator(), message);
63     }
64 
65     return (jlong)return_message;
66 }
67 
68 JNIEXPORT
Java_software_amazon_awssdk_crt_eventstream_Message_messageDelete(JNIEnv * env,jclass jni_class,jlong message_ptr)69 void JNICALL Java_software_amazon_awssdk_crt_eventstream_Message_messageDelete(
70     JNIEnv *env,
71     jclass jni_class,
72     jlong message_ptr) {
73     (void)env;
74     (void)jni_class;
75     aws_cache_jni_ids(env);
76 
77     struct aws_event_stream_message *message = (struct aws_event_stream_message *)message_ptr;
78     aws_event_stream_message_clean_up(message);
79     aws_mem_release(aws_jni_get_allocator(), message);
80 }
81 
82 JNIEXPORT
Java_software_amazon_awssdk_crt_eventstream_Message_messageBuffer(JNIEnv * env,jclass jni_class,jlong message_ptr)83 jobject JNICALL Java_software_amazon_awssdk_crt_eventstream_Message_messageBuffer(
84     JNIEnv *env,
85     jclass jni_class,
86     jlong message_ptr) {
87     (void)jni_class;
88     aws_cache_jni_ids(env);
89 
90     struct aws_event_stream_message *message = (struct aws_event_stream_message *)message_ptr;
91     const uint8_t *buffer = aws_event_stream_message_buffer(message);
92     size_t buffer_len = aws_event_stream_message_total_length(message);
93 
94     return aws_jni_direct_byte_buffer_from_raw_ptr(env, buffer, (jlong)buffer_len);
95 }
96 
aws_event_stream_rpc_marshall_message_args_init(struct aws_event_stream_rpc_marshalled_message * message_args,struct aws_allocator * allocator,JNIEnv * env,jbyteArray headers,jbyteArray payload,jbyteArray operation_name,jint message_flags,jint message_type)97 int aws_event_stream_rpc_marshall_message_args_init(
98     struct aws_event_stream_rpc_marshalled_message *message_args,
99     struct aws_allocator *allocator,
100     JNIEnv *env,
101     jbyteArray headers,
102     jbyteArray payload,
103     jbyteArray operation_name,
104     jint message_flags,
105     jint message_type) {
106     AWS_ZERO_STRUCT(*message_args);
107     message_args->allocator = allocator;
108 
109     if (headers) {
110         if (aws_event_stream_headers_list_init(&message_args->headers_list, allocator)) {
111             aws_jni_throw_runtime_exception(env, "EventStreamRPCMessage: headers allocation failed.");
112             return AWS_OP_ERR;
113         }
114 
115         message_args->headers_init = true;
116 
117         struct aws_byte_cursor headers_cur = aws_jni_byte_cursor_from_jbyteArray_acquire(env, headers);
118         /* copy because JNI is stupid and the buffer that the headers parser runs from needs the memory to stick around
119          * until the final message creation happens. */
120         aws_byte_buf_init_copy_from_cursor(&message_args->headers_buf, allocator, headers_cur);
121         int headers_parse_error = aws_event_stream_read_headers_from_buffer(
122             &message_args->headers_list, message_args->headers_buf.buffer, message_args->headers_buf.len);
123         aws_jni_byte_cursor_from_jbyteArray_release(env, headers, headers_cur);
124 
125         if (headers_parse_error) {
126             aws_jni_throw_runtime_exception(env, "EventStreamRPCMessage: headers allocation failed.");
127             goto clean_up;
128         }
129     }
130 
131     if (payload) {
132         struct aws_byte_cursor payload_cur = aws_jni_byte_cursor_from_jbyteArray_acquire(env, payload);
133         aws_byte_buf_init_copy_from_cursor(&message_args->payload_buf, allocator, payload_cur);
134         aws_jni_byte_cursor_from_jbyteArray_release(env, payload, payload_cur);
135 
136         if (!message_args->payload_buf.buffer) {
137             aws_jni_throw_runtime_exception(env, "EventStreamRPCMessage: allocation failed.");
138             goto clean_up;
139         }
140     }
141 
142     message_args->message_args.message_type = message_type;
143     message_args->message_args.message_flags = message_flags;
144     message_args->message_args.headers = message_args->headers_list.data;
145     message_args->message_args.headers_count = message_args->headers_list.length;
146     message_args->message_args.payload = &message_args->payload_buf;
147 
148     if (operation_name) {
149         struct aws_byte_cursor operation_cur = aws_jni_byte_cursor_from_jbyteArray_acquire(env, operation_name);
150         aws_byte_buf_init_copy_from_cursor(&message_args->operation_buf, allocator, operation_cur);
151         aws_jni_byte_cursor_from_jbyteArray_release(env, operation_name, operation_cur);
152 
153         if (!message_args->operation_buf.buffer) {
154             aws_jni_throw_runtime_exception(env, "CEventStreamRPCMessage: allocation failed.");
155             goto clean_up;
156         }
157     }
158 
159     return AWS_OP_SUCCESS;
160 
161 clean_up:
162     aws_byte_buf_clean_up(&message_args->headers_buf);
163     aws_byte_buf_clean_up(&message_args->payload_buf);
164     aws_byte_buf_clean_up(&message_args->operation_buf);
165 
166     if (message_args->headers_init) {
167         aws_event_stream_headers_list_cleanup(&message_args->headers_list);
168     }
169 
170     return AWS_OP_ERR;
171 }
172 
aws_event_stream_rpc_marshall_message_args_clean_up(struct aws_event_stream_rpc_marshalled_message * message_args)173 void aws_event_stream_rpc_marshall_message_args_clean_up(struct aws_event_stream_rpc_marshalled_message *message_args) {
174     aws_byte_buf_clean_up(&message_args->headers_buf);
175     aws_byte_buf_clean_up(&message_args->payload_buf);
176     aws_byte_buf_clean_up(&message_args->operation_buf);
177 
178     if (message_args->headers_init) {
179         aws_event_stream_headers_list_cleanup(&message_args->headers_list);
180         message_args->headers_init = false;
181     }
182 }
183 
aws_event_stream_rpc_marshall_headers_to_byteArray(struct aws_allocator * allocator,JNIEnv * env,struct aws_event_stream_header_value_pair * headers_array,size_t length)184 jbyteArray aws_event_stream_rpc_marshall_headers_to_byteArray(
185     struct aws_allocator *allocator,
186     JNIEnv *env,
187     struct aws_event_stream_header_value_pair *headers_array,
188     size_t length) {
189     /* this is not how we recommend you use the array_list api, but it is correct, and it prevents the need for extra
190      * allocations and copies. */
191     struct aws_array_list headers_list;
192     aws_array_list_init_static(&headers_list, headers_array, length, sizeof(struct aws_event_stream_header_value_pair));
193     headers_list.length = length;
194 
195     uint32_t headers_buf_len = aws_event_stream_compute_headers_required_buffer_len(&headers_list);
196 
197     struct aws_byte_buf headers_buf;
198     if (aws_byte_buf_init(&headers_buf, allocator, headers_buf_len)) {
199         return NULL;
200     }
201 
202     jbyteArray headers_byte_array = NULL;
203     if (aws_event_stream_write_headers_to_buffer_safe(&headers_list, &headers_buf)) {
204         goto done;
205     }
206 
207     struct aws_byte_cursor headers_cur = aws_byte_cursor_from_buf(&headers_buf);
208     headers_byte_array = aws_jni_byte_array_from_cursor(env, &headers_cur);
209 
210 done:
211 
212     aws_byte_buf_clean_up(&headers_buf);
213     aws_array_list_clean_up(&headers_list);
214 
215     return headers_byte_array;
216 }
217