@@ -611,7 +611,15 @@ JNIEXPORT jint JNICALL Java_software_amazon_awssdk_crt_http_HttpStream_httpStrea
611611
612612struct http_stream_write_data_callback_data {
613613 struct http_stream_binding * stream_cb_data ;
614- struct aws_byte_buf data_buf ;
614+ /*
615+ * Global ref to the Java byte[] passed in by the caller, and the aws_byte_cursor acquired
616+ * from it via aws_jni_byte_cursor_from_jbyteArray_acquire. Held on callback_data so the
617+ * JNI-side bytes stay valid for the full async write, and released from
618+ * s_cleanup_write_data_callback_data once the write completes (or fails synchronously).
619+ * Avoids copying the payload into a native aws_byte_buf before sending.
620+ */
621+ jbyteArray data_array ;
622+ struct aws_byte_cursor data_cur ;
615623 struct aws_input_stream * data_stream ;
616624 jobject completion_callback ;
617625};
@@ -622,7 +630,11 @@ static void s_cleanup_write_data_callback_data(
622630 if (callback_data -> data_stream ) {
623631 aws_input_stream_destroy (callback_data -> data_stream );
624632 }
625- aws_byte_buf_clean_up (& callback_data -> data_buf );
633+ if (callback_data -> data_array ) {
634+ /* Release the JNI pin/copy (needs the jbyteArray + original cursor), then drop our global ref. */
635+ aws_jni_byte_cursor_from_jbyteArray_release (env , callback_data -> data_array , callback_data -> data_cur );
636+ (* env )-> DeleteGlobalRef (env , callback_data -> data_array );
637+ }
626638 (* env )-> DeleteGlobalRef (env , callback_data -> completion_callback );
627639 aws_mem_release (aws_jni_get_allocator (), callback_data );
628640}
@@ -676,12 +688,19 @@ JNIEXPORT jint JNICALL Java_software_amazon_awssdk_crt_http_HttpStreamBase_httpS
676688 };
677689
678690 if (data != NULL ) {
679- struct aws_byte_cursor data_cur = aws_jni_byte_cursor_from_jbyteArray_acquire (env , data );
680- aws_byte_buf_init_copy_from_cursor (& callback_data -> data_buf , aws_jni_get_allocator (), data_cur );
681- aws_jni_byte_cursor_from_jbyteArray_release (env , data , data_cur );
682-
683- data_cur = aws_byte_cursor_from_buf (& callback_data -> data_buf );
684- callback_data -> data_stream = aws_input_stream_new_from_cursor (aws_jni_get_allocator (), & data_cur );
691+ /*
692+ * Promote to a global ref so it survives beyond this JNI frame — the async write
693+ * completion fires on the event-loop thread after this function returns.
694+ * Acquire the cursor once, store both on callback_data, and let
695+ * s_cleanup_write_data_callback_data release them when the write finishes.
696+ * aws_input_stream_new_from_cursor copies the {ptr,len} cursor struct into its own
697+ * state, but does NOT copy the underlying bytes, so this path avoids the payload copy.
698+ */
699+ callback_data -> data_array = (* env )-> NewGlobalRef (env , data );
700+ callback_data -> data_cur = aws_jni_byte_cursor_from_jbyteArray_acquire (env , data );
701+
702+ callback_data -> data_stream =
703+ aws_input_stream_new_from_cursor (aws_jni_get_allocator (), & callback_data -> data_cur );
685704 options .data = callback_data -> data_stream ;
686705 }
687706
0 commit comments