/** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ #include "auth.h" #include "checksums.h" #include "crypto.h" #include "event_stream.h" #include "http_connection.h" #include "http_connection_manager.h" #include "http_headers.h" #include "http_message.h" #include "http_stream.h" #include "io.h" #include "logger.h" #include "mqtt5_client.h" #include "mqtt_client.h" #include "mqtt_client_connection.h" #include "mqtt_request_response.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * This is a multi-line comment to ensure that the static assert does not collide with the static asserts in * aws/common/macro.h. * */ AWS_STATIC_ASSERT(NAPI_VERSION >= 4); #define AWS_DEFINE_ERROR_INFO_CRT_NODEJS(CODE, STR) AWS_DEFINE_ERROR_INFO(CODE, STR, "aws-crt-nodejs") /* TODO: * Hardcoded enum value for `napi_no_external_buffers_allowed`. * The enum `napi_no_external_buffers_allowed` is introduced in node21 and backport * to node 14.21.2, 16.19.0, 18.13.0. * Use `napi_no_external_buffers_allowed` for external buffer related changes after bump to node 21 */ #define NAPI_NO_EXTERNAL_BUFFER_ENUM_VALUE 22 static bool s_tsfn_enabled = false; static struct aws_rw_lock s_tsfn_lock; /* clang-format off */ static struct aws_error_info s_errors[] = { AWS_DEFINE_ERROR_INFO_CRT_NODEJS( AWS_CRT_NODEJS_ERROR_THREADSAFE_FUNCTION_NULL_NAPI_ENV, "There was an attempt to execute a thread-safe napi function binding with a null napi environment. This is usually due to the function binding being released by a shutdown/cleanup process while the execution is waiting in the queue."), AWS_DEFINE_ERROR_INFO_CRT_NODEJS( AWS_CRT_NODEJS_ERROR_NAPI_FAILURE, "A N-API API call failed"), AWS_DEFINE_ERROR_INFO_CRT_NODEJS( AWS_CRT_NODEJS_ERROR_EVENT_STREAM_USER_CLOSE, "User invoked close on an eventstream connection."), }; /* clang-format on */ static struct aws_error_info_list s_error_list = { .error_list = s_errors, .count = sizeof(s_errors) / sizeof(struct aws_error_info), }; static struct aws_log_subject_info s_log_subject_infos[] = { DEFINE_LOG_SUBJECT_INFO(AWS_LS_NODEJS_CRT_GENERAL, "node", "General Node/N-API events"), }; static struct aws_log_subject_info_list s_log_subject_list = { .subject_list = s_log_subject_infos, .count = AWS_ARRAY_SIZE(s_log_subject_infos), }; static uv_loop_t *s_node_uv_loop = NULL; static struct aws_event_loop *s_node_uv_event_loop = NULL; static struct aws_event_loop_group *s_node_uv_elg = NULL; static struct aws_host_resolver *s_default_host_resolver = NULL; static struct aws_client_bootstrap *s_default_client_bootstrap = NULL; int aws_napi_attach_object_property_boolean(napi_value object, napi_env env, const char *key_name, bool value) { if (key_name == NULL) { return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); } napi_value napi_boolean = NULL; AWS_NAPI_CALL(env, napi_get_boolean(env, value, &napi_boolean), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); AWS_NAPI_CALL(env, napi_set_named_property(env, object, key_name, napi_boolean), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); return AWS_OP_SUCCESS; } int aws_napi_attach_object_property_optional_boolean( napi_value object, napi_env env, const char *key_name, const bool *value) { if (value == NULL) { return AWS_OP_SUCCESS; } return aws_napi_attach_object_property_boolean(object, env, key_name, *value); } /* * IEEE fp double analysis says 2 ^ 53 is the maximum sequential value we could support, but the napi docs for * napi_create_int64() say (2 ^ 53 - 1) so we'll use their more conservative bound. */ #define MAX_ALLOWED_UINT64_T_TO_DOUBLE ((((uint64_t)1) << 53) - 1) int aws_napi_attach_object_property_u64(napi_value object, napi_env env, const char *key_name, uint64_t value) { if (key_name == NULL) { return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); } if (value > MAX_ALLOWED_UINT64_T_TO_DOUBLE) { return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); } napi_value napi_i64 = NULL; AWS_NAPI_CALL(env, napi_create_int64(env, (int64_t)value, &napi_i64), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); AWS_NAPI_CALL(env, napi_set_named_property(env, object, key_name, napi_i64), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); return AWS_OP_SUCCESS; } int aws_napi_attach_object_property_optional_u64( napi_value object, napi_env env, const char *key_name, const uint64_t *value) { if (value == NULL) { return AWS_OP_SUCCESS; } return aws_napi_attach_object_property_u64(object, env, key_name, *value); } int aws_napi_attach_object_property_u32(napi_value object, napi_env env, const char *key_name, uint32_t value) { if (key_name == NULL) { return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); } napi_value napi_u32 = NULL; AWS_NAPI_CALL( env, napi_create_uint32(env, value, &napi_u32), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); AWS_NAPI_CALL(env, napi_set_named_property(env, object, key_name, napi_u32), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); return AWS_OP_SUCCESS; } int aws_napi_attach_object_property_optional_u32( napi_value object, napi_env env, const char *key_name, const uint32_t *value) { if (value == NULL) { return AWS_OP_SUCCESS; } return aws_napi_attach_object_property_u32(object, env, key_name, *value); } int aws_napi_attach_object_property_i32(napi_value object, napi_env env, const char *key_name, int32_t value) { if (key_name == NULL) { return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); } napi_value napi_i32 = NULL; AWS_NAPI_CALL( env, napi_create_int32(env, value, &napi_i32), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); AWS_NAPI_CALL(env, napi_set_named_property(env, object, key_name, napi_i32), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); return AWS_OP_SUCCESS; } int aws_napi_attach_object_property_u16(napi_value object, napi_env env, const char *key_name, uint16_t value) { return aws_napi_attach_object_property_u32(object, env, key_name, (uint32_t)value); } int aws_napi_attach_object_property_optional_u16( napi_value object, napi_env env, const char *key_name, const uint16_t *value) { if (value == NULL) { return AWS_OP_SUCCESS; } return aws_napi_attach_object_property_u16(object, env, key_name, *value); } int aws_napi_attach_object_property_string( napi_value object, napi_env env, const char *key_name, struct aws_byte_cursor value) { if (key_name == NULL) { return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); } napi_value napi_string = NULL; AWS_NAPI_CALL(env, napi_create_string_utf8(env, (const char *)(value.ptr), value.len, &napi_string), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); AWS_NAPI_CALL(env, napi_set_named_property(env, object, key_name, napi_string), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); return AWS_OP_SUCCESS; } int aws_napi_attach_object_property_optional_string( napi_value object, napi_env env, const char *key_name, const struct aws_byte_cursor *value) { if (value == NULL) { return AWS_OP_SUCCESS; } return aws_napi_attach_object_property_string(object, env, key_name, *value); } static void s_finalize_external_binary_byte_buf(napi_env env, void *finalize_data, void *finalize_hint) { (void)env; (void)finalize_data; struct aws_byte_buf *buffer = finalize_hint; if (buffer == NULL) { return; } struct aws_allocator *allocator = buffer->allocator; aws_byte_buf_clean_up(buffer); aws_mem_release(allocator, buffer); } int aws_napi_attach_object_property_binary_as_finalizable_external( napi_value object, napi_env env, const char *key_name, struct aws_byte_buf *data_buffer) { if (key_name == NULL) { return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); } napi_value napi_binary = NULL; AWS_NAPI_ENSURE( env, aws_napi_create_external_arraybuffer( env, data_buffer->buffer, data_buffer->len, s_finalize_external_binary_byte_buf, data_buffer, &napi_binary)); AWS_NAPI_CALL(env, napi_set_named_property(env, object, key_name, napi_binary), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); return AWS_OP_SUCCESS; } enum aws_napi_get_named_property_result aws_napi_get_named_property( napi_env env, napi_value object, const char *name, napi_valuetype type, napi_value *result) { if (name == NULL) { return AWS_NGNPR_NO_VALUE; } bool has_property = false; if (napi_has_named_property(env, object, name, &has_property) || !has_property) { return AWS_NGNPR_NO_VALUE; } napi_value property = NULL; if (napi_get_named_property(env, object, name, &property)) { return AWS_NGNPR_NO_VALUE; } if (type != napi_undefined) { napi_valuetype property_type = napi_undefined; if (napi_typeof(env, property, &property_type)) { return AWS_NGNPR_INVALID_VALUE; } if (property_type != type) { return AWS_NGNPR_INVALID_VALUE; } } *result = property; return AWS_NGNPR_VALID_VALUE; } enum aws_napi_get_named_property_result aws_napi_get_named_property_as_uint16( napi_env env, napi_value object, const char *name, uint16_t *result) { napi_value node_result; enum aws_napi_get_named_property_result get_property_result = aws_napi_get_named_property(env, object, name, napi_number, &node_result); if (get_property_result != AWS_NGNPR_VALID_VALUE) { return get_property_result; } int64_t int_value = 0; AWS_NAPI_CALL(env, napi_get_value_int64(env, node_result, &int_value), { return AWS_NGNPR_INVALID_VALUE; }); if (int_value < 0 || int_value > UINT16_MAX) { return AWS_NGNPR_INVALID_VALUE; } *result = (uint16_t)int_value; return AWS_NGNPR_VALID_VALUE; } enum aws_napi_get_named_property_result aws_napi_get_named_property_as_uint32( napi_env env, napi_value object, const char *name, uint32_t *result) { napi_value node_result; enum aws_napi_get_named_property_result get_property_result = aws_napi_get_named_property(env, object, name, napi_number, &node_result); if (get_property_result != AWS_NGNPR_VALID_VALUE) { return get_property_result; } int64_t int_value = 0; AWS_NAPI_CALL(env, napi_get_value_int64(env, node_result, &int_value), { return AWS_NGNPR_INVALID_VALUE; }); if (int_value < 0 || int_value > UINT32_MAX) { return AWS_NGNPR_INVALID_VALUE; } *result = (uint32_t)int_value; return AWS_NGNPR_VALID_VALUE; } enum aws_napi_get_named_property_result aws_napi_get_named_property_as_uint64( napi_env env, napi_value object, const char *name, uint64_t *result) { napi_value node_result; enum aws_napi_get_named_property_result get_property_result = aws_napi_get_named_property(env, object, name, napi_number, &node_result); if (get_property_result != AWS_NGNPR_VALID_VALUE) { return get_property_result; } int64_t int_value = 0; AWS_NAPI_CALL(env, napi_get_value_int64(env, node_result, &int_value), { return AWS_NGNPR_INVALID_VALUE; }); if (int_value < 0) { return AWS_NGNPR_INVALID_VALUE; } *result = (uint64_t)int_value; return AWS_NGNPR_VALID_VALUE; } enum aws_napi_get_named_property_result aws_napi_get_named_property_as_uint8( napi_env env, napi_value object, const char *name, uint8_t *result) { napi_value node_result; enum aws_napi_get_named_property_result get_property_result = aws_napi_get_named_property(env, object, name, napi_number, &node_result); if (get_property_result != AWS_NGNPR_VALID_VALUE) { return get_property_result; } int64_t int_value = 0; AWS_NAPI_CALL(env, napi_get_value_int64(env, node_result, &int_value), { return AWS_NGNPR_INVALID_VALUE; }); if (int_value < 0 || int_value > UINT8_MAX) { return AWS_NGNPR_INVALID_VALUE; } *result = (uint8_t)int_value; return AWS_NGNPR_VALID_VALUE; } enum aws_napi_get_named_property_result aws_napi_get_named_property_as_int8( napi_env env, napi_value object, const char *name, int8_t *result) { napi_value node_result; enum aws_napi_get_named_property_result get_property_result = aws_napi_get_named_property(env, object, name, napi_number, &node_result); if (get_property_result != AWS_NGNPR_VALID_VALUE) { return get_property_result; } int64_t int_value = 0; AWS_NAPI_CALL(env, napi_get_value_int64(env, node_result, &int_value), { return AWS_NGNPR_INVALID_VALUE; }); if (int_value < INT8_MIN || int_value > INT8_MAX) { return AWS_NGNPR_INVALID_VALUE; } *result = (int8_t)int_value; return AWS_NGNPR_VALID_VALUE; } enum aws_napi_get_named_property_result aws_napi_get_named_property_as_int16( napi_env env, napi_value object, const char *name, int16_t *result) { napi_value node_result; enum aws_napi_get_named_property_result get_property_result = aws_napi_get_named_property(env, object, name, napi_number, &node_result); if (get_property_result != AWS_NGNPR_VALID_VALUE) { return get_property_result; } int64_t int_value = 0; AWS_NAPI_CALL(env, napi_get_value_int64(env, node_result, &int_value), { return AWS_NGNPR_INVALID_VALUE; }); if (int_value < INT16_MIN || int_value > INT16_MAX) { return AWS_NGNPR_INVALID_VALUE; } *result = (int16_t)int_value; return AWS_NGNPR_VALID_VALUE; } enum aws_napi_get_named_property_result aws_napi_get_named_property_as_int32( napi_env env, napi_value object, const char *name, int32_t *result) { napi_value node_result; enum aws_napi_get_named_property_result get_property_result = aws_napi_get_named_property(env, object, name, napi_number, &node_result); if (get_property_result != AWS_NGNPR_VALID_VALUE) { return get_property_result; } int64_t int_value = 0; AWS_NAPI_CALL(env, napi_get_value_int64(env, node_result, &int_value), { return AWS_NGNPR_INVALID_VALUE; }); if (int_value < INT32_MIN || int_value > INT32_MAX) { return AWS_NGNPR_INVALID_VALUE; } *result = (int32_t)int_value; return AWS_NGNPR_VALID_VALUE; } enum aws_napi_get_named_property_result aws_napi_get_named_property_as_int64( napi_env env, napi_value object, const char *name, int64_t *result) { napi_value node_result; enum aws_napi_get_named_property_result get_property_result = aws_napi_get_named_property(env, object, name, napi_number, &node_result); if (get_property_result != AWS_NGNPR_VALID_VALUE) { return get_property_result; } AWS_NAPI_CALL(env, napi_get_value_int64(env, node_result, result), { return AWS_NGNPR_INVALID_VALUE; }); return AWS_NGNPR_VALID_VALUE; } enum aws_napi_get_named_property_result aws_napi_get_named_property_as_boolean( napi_env env, napi_value object, const char *name, bool *result) { napi_value node_result; enum aws_napi_get_named_property_result get_property_result = aws_napi_get_named_property(env, object, name, napi_boolean, &node_result); if (get_property_result != AWS_NGNPR_VALID_VALUE) { return get_property_result; } AWS_NAPI_CALL(env, napi_get_value_bool(env, node_result, result), { return AWS_NGNPR_INVALID_VALUE; }); return AWS_NGNPR_VALID_VALUE; } enum aws_napi_get_named_property_result aws_napi_get_named_property_boolean_as_uint8( napi_env env, napi_value object, const char *name, uint8_t *result) { napi_value node_result; enum aws_napi_get_named_property_result get_property_result = aws_napi_get_named_property(env, object, name, napi_boolean, &node_result); if (get_property_result != AWS_NGNPR_VALID_VALUE) { return get_property_result; } bool bool_result = false; AWS_NAPI_CALL(env, napi_get_value_bool(env, node_result, &bool_result), { return AWS_NGNPR_INVALID_VALUE; }); *result = bool_result ? 1 : 0; return AWS_NGNPR_VALID_VALUE; } enum aws_napi_get_named_property_result aws_napi_get_named_property_as_bytebuf( napi_env env, napi_value object, const char *name, napi_valuetype type, struct aws_byte_buf *result) { napi_value node_result; enum aws_napi_get_named_property_result get_property_result = aws_napi_get_named_property(env, object, name, type, &node_result); if (get_property_result != AWS_NGNPR_VALID_VALUE) { return get_property_result; } AWS_NAPI_CALL(env, aws_byte_buf_init_from_napi(result, env, node_result), { return AWS_NGNPR_INVALID_VALUE; }); return AWS_NGNPR_VALID_VALUE; } enum aws_napi_get_named_property_result aws_napi_get_named_property_buffer_length( napi_env env, napi_value object, const char *name, napi_valuetype type, size_t *length_out) { struct aws_byte_buf buffer; AWS_ZERO_STRUCT(buffer); enum aws_napi_get_named_property_result result = aws_napi_get_named_property_as_bytebuf(env, object, name, type, &buffer); if (result == AWS_NGNPR_VALID_VALUE) { *length_out = buffer.len; } aws_byte_buf_clean_up(&buffer); return result; } static int s_typed_array_element_type_to_byte_length(napi_typedarray_type type, size_t *element_length) { switch (type) { case napi_int8_array: case napi_uint8_array: case napi_uint8_clamped_array: *element_length = 1; return AWS_OP_SUCCESS; case napi_int16_array: case napi_uint16_array: *element_length = 2; return AWS_OP_SUCCESS; case napi_int32_array: case napi_uint32_array: case napi_float32_array: *element_length = 4; return AWS_OP_SUCCESS; case napi_float64_array: case 9: /*napi_bigint64_array */ case 10: /*napi_biguint64_array*/ *element_length = 8; return AWS_OP_SUCCESS; } return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); } napi_status aws_byte_buf_init_from_napi(struct aws_byte_buf *buf, napi_env env, napi_value node_str) { AWS_ASSERT(buf); napi_valuetype type = napi_undefined; AWS_NAPI_CALL(env, napi_typeof(env, node_str, &type), { return status; }); if (type == napi_string) { size_t length = 0; AWS_NAPI_CALL(env, napi_get_value_string_utf8(env, node_str, NULL, 0, &length), { return status; }); /* Node requires that the null terminator be written */ if (aws_byte_buf_init(buf, aws_napi_get_allocator(), length + 1)) { return napi_generic_failure; } AWS_NAPI_CALL(env, napi_get_value_string_utf8(env, node_str, (char *)buf->buffer, buf->capacity, &buf->len), { return status; }); AWS_ASSERT(length == buf->len); return napi_ok; } else if (type == napi_object) { bool is_expected = false; /* Try ArrayBuffer */ AWS_NAPI_CALL(env, napi_is_arraybuffer(env, node_str, &is_expected), { return status; }); if (is_expected) { napi_status status = napi_get_arraybuffer_info(env, node_str, (void **)&buf->buffer, &buf->len); buf->capacity = buf->len; return status; } /* Try DataView */ AWS_NAPI_CALL(env, napi_is_dataview(env, node_str, &is_expected), { return status; }); if (is_expected) { napi_status status = napi_get_dataview_info(env, node_str, &buf->len, (void **)&buf->buffer, NULL, NULL); buf->capacity = buf->len; return status; } /* Try TypedArray */ AWS_NAPI_CALL(env, napi_is_typedarray(env, node_str, &is_expected), { return status; }); if (is_expected) { napi_typedarray_type array_type = napi_uint8_array; size_t length = 0; AWS_NAPI_CALL( env, napi_get_typedarray_info(env, node_str, &array_type, &length, (void **)&buf->buffer, NULL, NULL), { return status; }); size_t element_size = 0; if (s_typed_array_element_type_to_byte_length(array_type, &element_size)) { return napi_invalid_arg; } buf->len = length * element_size; buf->capacity = buf->len; return napi_ok; } } return napi_invalid_arg; } int aws_napi_value_get_storage_length(napi_env env, napi_value value, size_t *storage_length) { napi_valuetype type = napi_undefined; AWS_NAPI_CALL(env, napi_typeof(env, value, &type), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); if (type == napi_string) { AWS_NAPI_CALL(env, napi_get_value_string_utf8(env, value, NULL, 0, storage_length), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); return AWS_OP_SUCCESS; } else if (type == napi_object) { /* Try ArrayBuffer */ bool is_array_buffer = false; AWS_NAPI_CALL(env, napi_is_arraybuffer(env, value, &is_array_buffer), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); if (is_array_buffer) { void *buffer = NULL; AWS_NAPI_CALL(env, napi_get_arraybuffer_info(env, value, &buffer, storage_length), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); return AWS_OP_SUCCESS; } /* Try DataView */ bool is_data_view = false; AWS_NAPI_CALL(env, napi_is_dataview(env, value, &is_data_view), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); if (is_data_view) { AWS_NAPI_CALL(env, napi_get_dataview_info(env, value, storage_length, NULL, NULL, NULL), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); return AWS_OP_SUCCESS; } /* Try TypedArray */ bool is_typed_array = false; AWS_NAPI_CALL(env, napi_is_typedarray(env, value, &is_typed_array), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); if (is_typed_array) { napi_typedarray_type array_type = napi_uint8_array; size_t length = 0; AWS_NAPI_CALL(env, napi_get_typedarray_info(env, value, &array_type, &length, NULL, NULL, NULL), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); size_t element_size = 0; if (s_typed_array_element_type_to_byte_length(array_type, &element_size)) { return napi_invalid_arg; } *storage_length = length * element_size; return AWS_OP_SUCCESS; } } return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); } int aws_napi_value_bytebuf_append( napi_env env, napi_value value, struct aws_byte_buf *output_buffer, struct aws_byte_cursor *bytes_written_cursor) { AWS_ZERO_STRUCT(*bytes_written_cursor); napi_valuetype type = napi_undefined; AWS_NAPI_CALL(env, napi_typeof(env, value, &type), { return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); }); size_t open_bytes = output_buffer->capacity - output_buffer->len; uint8_t *open_space = output_buffer->buffer + output_buffer->len; if (type == napi_string) { size_t bytes_written = 0; AWS_NAPI_CALL(env, napi_get_value_string_utf8(env, value, (char *)open_space, open_bytes, &bytes_written), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); /* technically, we might have been truncated, but there's no way to tell */ bytes_written_cursor->ptr = open_space; bytes_written_cursor->len = bytes_written; output_buffer->len += bytes_written; return AWS_OP_SUCCESS; } else if (type == napi_object) { /* Try ArrayBuffer */ bool is_array_buffer = false; AWS_NAPI_CALL(env, napi_is_arraybuffer(env, value, &is_array_buffer), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); if (is_array_buffer) { void *buffer = NULL; size_t buffer_length = 0; AWS_NAPI_CALL(env, napi_get_arraybuffer_info(env, value, &buffer, &buffer_length), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); bytes_written_cursor->ptr = buffer; bytes_written_cursor->len = buffer_length; return aws_byte_buf_append_and_update(output_buffer, bytes_written_cursor); } /* Try DataView */ bool is_data_view = false; AWS_NAPI_CALL(env, napi_is_dataview(env, value, &is_data_view), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); if (is_data_view) { void *buffer = NULL; size_t buffer_length = 0; AWS_NAPI_CALL(env, napi_get_dataview_info(env, value, &buffer_length, &buffer, NULL, NULL), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); bytes_written_cursor->ptr = buffer; bytes_written_cursor->len = buffer_length; return aws_byte_buf_append_and_update(output_buffer, bytes_written_cursor); } bool is_typed_array = false; AWS_NAPI_CALL(env, napi_is_typedarray(env, value, &is_typed_array), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); if (is_typed_array) { napi_typedarray_type array_type = napi_uint8_array; size_t length = 0; uint8_t *buffer = NULL; AWS_NAPI_CALL( env, napi_get_typedarray_info(env, value, &array_type, &length, (void **)&buffer, NULL, NULL), { return aws_raise_error(AWS_CRT_NODEJS_ERROR_NAPI_FAILURE); }); size_t element_size = 0; if (s_typed_array_element_type_to_byte_length(array_type, &element_size)) { return napi_invalid_arg; } bytes_written_cursor->ptr = buffer; bytes_written_cursor->len = element_size * length; return aws_byte_buf_append_and_update(output_buffer, bytes_written_cursor); } } return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); } struct aws_string *aws_string_new_from_napi(napi_env env, napi_value node_str) { struct aws_byte_buf temp_buf; if (aws_byte_buf_init_from_napi(&temp_buf, env, node_str)) { return NULL; } struct aws_string *string = aws_string_new_from_array(aws_napi_get_allocator(), temp_buf.buffer, temp_buf.len); aws_byte_buf_clean_up(&temp_buf); return string; } napi_status aws_napi_create_dataview_from_byte_cursor( napi_env env, const struct aws_byte_cursor *cur, napi_value *result) { void *data = NULL; napi_value arraybuffer; AWS_NAPI_CALL(env, napi_create_arraybuffer(env, cur->len, &data, &arraybuffer), { return status; }); struct aws_byte_buf arraybuffer_buf = aws_byte_buf_from_empty_array(data, cur->len); struct aws_byte_cursor input = *cur; if (!aws_byte_buf_write_from_whole_cursor(&arraybuffer_buf, input)) { return napi_generic_failure; } AWS_NAPI_CALL(env, napi_create_dataview(env, cur->len, arraybuffer, 0, result), { return status; }); return napi_ok; } bool aws_napi_is_null_or_undefined(napi_env env, napi_value value) { napi_valuetype type = napi_undefined; if (napi_ok != napi_typeof(env, value, &type)) { return true; } return type == napi_null || type == napi_undefined; } int aws_napi_get_property_array_size( napi_env env, napi_value object, const char *property_name, size_t *array_size_out) { napi_value napi_array = NULL; enum aws_napi_get_named_property_result get_result = aws_napi_get_named_property(env, object, property_name, napi_object, &napi_array); if (get_result == AWS_NGNPR_NO_VALUE) { *array_size_out = 0; return AWS_OP_SUCCESS; } else if (get_result == AWS_NGNPR_INVALID_VALUE) { return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); } uint32_t array_size = 0; AWS_NAPI_CALL(env, napi_get_array_length(env, napi_array, &array_size), { return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); }); *array_size_out = (size_t)array_size; return AWS_OP_SUCCESS; } void s_aws_enable_threadsafe_function(void) { aws_rw_lock_wlock(&s_tsfn_lock); s_tsfn_enabled = true; aws_rw_lock_wunlock(&s_tsfn_lock); } void s_aws_disable_threadsafe_function(void) { aws_rw_lock_wlock(&s_tsfn_lock); s_tsfn_enabled = false; aws_rw_lock_wunlock(&s_tsfn_lock); } napi_value aws_napi_disable_threadsafe_function(napi_env env, napi_callback_info info) { (void)info; if (env == NULL) { aws_raise_error(AWS_CRT_NODEJS_ERROR_THREADSAFE_FUNCTION_NULL_NAPI_ENV); return NULL; } s_aws_disable_threadsafe_function(); return NULL; } void aws_napi_throw_last_error(napi_env env) { const int error_code = aws_last_error(); napi_throw_error(env, aws_error_str(error_code), aws_error_debug_str(error_code)); } void aws_napi_throw_last_error_with_context(napi_env env, const char *context) { const int error_code = aws_last_error(); char full_msg[1024]; snprintf( full_msg, AWS_ARRAY_SIZE(full_msg), "%s : (%s - %s)", context, aws_error_str(error_code), aws_error_debug_str(error_code)); napi_throw_error(env, aws_error_str(error_code), full_msg); } struct uv_loop_s *aws_napi_get_node_uv_loop(void) { return s_node_uv_loop; } struct aws_event_loop *aws_napi_get_node_event_loop(void) { return s_node_uv_event_loop; } struct aws_event_loop_group *aws_napi_get_node_elg(void) { return s_node_uv_elg; } struct aws_client_bootstrap *aws_napi_get_default_client_bootstrap(void) { return s_default_client_bootstrap; } /* The napi_status enum has grown, and is not bound by N-API versioning */ #if defined(__clang__) || defined(__GNUC__) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wswitch" #endif const char *aws_napi_status_to_str(napi_status status) { const char *reason = "UNKNOWN"; switch (status) { case napi_ok: reason = "OK"; break; case napi_invalid_arg: reason = "napi_invalid_arg: an invalid argument was supplied"; break; case napi_object_expected: reason = "napi_object_expected"; break; case napi_string_expected: reason = "napi_name_expected"; break; case napi_name_expected: reason = "napi_name_expected"; break; case napi_function_expected: reason = "napi_function_expected"; break; case napi_number_expected: reason = "napi_number_expected"; break; case napi_boolean_expected: reason = "napi_boolean_expected"; break; case napi_array_expected: reason = "napi_array_expected"; break; case napi_generic_failure: reason = "napi_generic_failure"; break; case napi_pending_exception: reason = "napi_pending_exception"; break; case napi_cancelled: reason = "napi_cancelled"; break; case napi_escape_called_twice: reason = "napi_escape_called_twice"; break; case napi_handle_scope_mismatch: reason = "napi_handle_scope_mismatch"; break; case napi_callback_scope_mismatch: reason = "napi_callback_scope_mismatch"; break; case napi_queue_full: reason = "napi_queue_full"; break; case napi_closing: reason = "napi_closing"; break; case napi_bigint_expected: reason = "napi_bigint_expected"; break; case NAPI_NO_EXTERNAL_BUFFER_ENUM_VALUE: reason = "napi_no_external_buffers_allowed"; break; } return reason; } #if defined(__clang__) || defined(__GNUC__) # pragma GCC diagnostic pop #endif static void s_handle_failed_callback(napi_env env, napi_value function, napi_status reason) { /* Figure out if there's an exception pending, if so, no callbacks will ever succeed again until it's cleared */ bool pending_exception = reason == napi_pending_exception; AWS_NAPI_ENSURE(env, napi_is_exception_pending(env, &pending_exception)); /* if there's no pending exception, but failure occurred, log what we can find and get out */ if (!pending_exception) { const napi_extended_error_info *node_error_info = NULL; AWS_NAPI_ENSURE(env, napi_get_last_error_info(env, &node_error_info)); AWS_NAPI_LOGF_ERROR( "Extended error info: engine_error_code=%u error_code=%s error_message=%s", node_error_info->engine_error_code, aws_napi_status_to_str(node_error_info->error_code), node_error_info->error_message); return; } /* get the current exception and report it, and clear it so that execution can continue */ napi_value node_exception = NULL; AWS_NAPI_ENSURE(env, napi_get_and_clear_last_exception(env, &node_exception)); /* figure out what the exception is */ bool is_error = false; AWS_NAPI_ENSURE(env, napi_is_error(env, node_exception, &is_error)); /* * Convert the function to a string. If it's a lambda, this will produce the source of the lambda, if * it's a class function or free function, it will produce the name */ napi_value node_function_str = NULL; AWS_NAPI_ENSURE(env, napi_coerce_to_string(env, function, &node_function_str)); struct aws_string *function_str = aws_string_new_from_napi(env, node_function_str); if (function_str) { AWS_NAPI_LOGF_ERROR("Calling %s", aws_string_c_str(function_str)); } /* If it's an Error, extract info from it and log it */ if (is_error) { /* get the Error.message field */ napi_value node_message = NULL; AWS_NAPI_ENSURE(env, napi_get_named_property(env, node_exception, "message", &node_message)); /* extract and log the message */ struct aws_string *message = aws_string_new_from_napi(env, node_message); if (message) { AWS_NAPI_LOGF_ERROR("Error: %s", aws_string_bytes(message)); aws_string_destroy(message); } else { AWS_NAPI_LOGF_ERROR("aws_string_new_from_napi(exception.message) failed"); return; } /* get the Error.stack field */ napi_value node_stack = NULL; AWS_NAPI_ENSURE(env, napi_get_named_property(env, node_exception, "stack", &node_stack)); /* extract and log the stack trace */ struct aws_string *stacktrace = aws_string_new_from_napi(env, node_stack); if (stacktrace) { AWS_NAPI_LOGF_ERROR("Stack:\n%s", aws_string_bytes(stacktrace)); aws_string_destroy(stacktrace); } else { AWS_NAPI_LOGF_ERROR("aws_string_new_from_napi(exception.stack) failed"); return; } /* the Error has been reported and cleared, that's all we can do */ return; } /* The last thing thrown was some other sort of object/primitive, so convert it to a string and log it */ napi_value node_error_str = NULL; AWS_NAPI_ENSURE(env, napi_coerce_to_string(env, node_exception, &node_error_str)); struct aws_string *error_str = aws_string_new_from_napi(env, node_error_str); if (error_str) { AWS_NAPI_LOGF_ERROR("Error: %s", aws_string_bytes(error_str)); } else { AWS_NAPI_LOGF_ERROR("aws_string_new_from_napi(ToString(exception)) failed"); return; } } napi_status aws_napi_create_external_arraybuffer( napi_env env, void *external_data, size_t byte_length, napi_finalize finalize_cb, void *finalize_hint, napi_value *result) { napi_status external_buffer_status = napi_create_external_arraybuffer(env, external_data, byte_length, finalize_cb, finalize_hint, result); if (external_buffer_status == NAPI_NO_EXTERNAL_BUFFER_ENUM_VALUE) { // The external buffer is disabled, manually copy the external_data into Node void *napi_buf_data = NULL; napi_status create_arraybuffer_status = napi_create_arraybuffer(env, byte_length, &napi_buf_data, result); if (create_arraybuffer_status != napi_ok) { AWS_NAPI_LOGF_ERROR( "napi_create_arraybuffer (in aws_napi_create_external_arraybuffer) failed with : %s", aws_napi_status_to_str(create_arraybuffer_status)); return create_arraybuffer_status; } memcpy(napi_buf_data, external_data, byte_length); // As the data has been copied into the Node, invoke the finalize callback to make sure the // data is released. finalize_cb(env, finalize_hint, finalize_hint); } return napi_ok; } napi_status aws_napi_dispatch_threadsafe_function( napi_env env, napi_threadsafe_function tsfn, napi_value this_ptr, napi_value function, size_t argc, napi_value *argv) { aws_rw_lock_rlock(&s_tsfn_lock); napi_status result = napi_ok; if (s_tsfn_enabled) { napi_status call_status = napi_ok; if (!this_ptr) { AWS_NAPI_ENSURE(env, napi_get_undefined(env, &this_ptr)); } AWS_NAPI_CALL(env, napi_call_function(env, this_ptr, function, argc, argv, NULL), { call_status = status; s_handle_failed_callback(env, function, status); }); /* main thread can exit now */ napi_unref_threadsafe_function(env, tsfn); /* Must always decrement the ref count, or the function will be pinned */ napi_status release_status = napi_release_threadsafe_function(tsfn, napi_tsfn_release); result = (call_status != napi_ok) ? call_status : release_status; } aws_rw_lock_runlock(&s_tsfn_lock); return result; } napi_status aws_napi_create_threadsafe_function( napi_env env, napi_value function, const char *name, napi_threadsafe_function_call_js call_js, void *context, napi_threadsafe_function *result) { napi_value resource_name = NULL; AWS_NAPI_ENSURE(env, napi_create_string_utf8(env, name, NAPI_AUTO_LENGTH, &resource_name)); return napi_create_threadsafe_function( env, function, NULL /*async_resource*/, resource_name, 0 /*max_queue_size - 0 means no limit*/, 1 /*initial_thread_count*/, NULL /*thread_finalize_data*/, NULL /*thread_finalize_cb*/, context, call_js, result); } napi_status aws_napi_release_threadsafe_function( napi_threadsafe_function function, napi_threadsafe_function_release_mode mode) { napi_status result = napi_ok; aws_rw_lock_rlock(&s_tsfn_lock); if (s_tsfn_enabled && function) { result = napi_release_threadsafe_function(function, mode); } aws_rw_lock_runlock(&s_tsfn_lock); return result; } napi_status aws_napi_acquire_threadsafe_function(napi_threadsafe_function function) { napi_status result = napi_ok; aws_rw_lock_rlock(&s_tsfn_lock); if (s_tsfn_enabled && function) { result = napi_acquire_threadsafe_function(function); } aws_rw_lock_runlock(&s_tsfn_lock); return result; } napi_status aws_napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function function) { napi_status result = napi_ok; aws_rw_lock_rlock(&s_tsfn_lock); if (s_tsfn_enabled && function) { result = napi_unref_threadsafe_function(env, function); } aws_rw_lock_runlock(&s_tsfn_lock); return result; } napi_status aws_napi_queue_threadsafe_function(napi_threadsafe_function function, void *user_data) { napi_status result = napi_ok; aws_rw_lock_rlock(&s_tsfn_lock); if (s_tsfn_enabled && function) { /* increase the ref count, gets decreased when the call completes */ AWS_NAPI_ENSURE(NULL, napi_acquire_threadsafe_function(function)); result = napi_call_threadsafe_function(function, user_data, napi_tsfn_nonblocking); } aws_rw_lock_runlock(&s_tsfn_lock); return result; } AWS_STATIC_STRING_FROM_LITERAL(s_mem_tracing_env_var, "AWS_CRT_MEMORY_TRACING"); static struct aws_allocator *s_allocator = NULL; struct aws_allocator *aws_napi_get_allocator(void) { if (AWS_UNLIKELY(s_allocator == NULL)) { struct aws_string *value = NULL; if (aws_get_environment_value(aws_default_allocator(), s_mem_tracing_env_var, &value) || value == NULL) { return s_allocator = aws_default_allocator(); } int level = atoi(aws_string_c_str(value)); if (level < AWS_MEMTRACE_NONE || level > AWS_MEMTRACE_STACKS) { /* this can't go through logging, because it happens before logging is set up */ fprintf( stderr, "AWS_CRT_MEMORY_TRACING is set to invalid value: %s, must be 0 (none), 1 (bytes), or 2 (stacks)", aws_string_bytes(value)); level = AWS_MEMTRACE_NONE; } s_allocator = aws_mem_tracer_new(aws_default_allocator(), NULL, level, 16); } return s_allocator; } napi_value aws_napi_native_memory(napi_env env, napi_callback_info info) { (void)info; napi_value node_allocated = NULL; size_t allocated = 0; if (aws_napi_get_allocator() != aws_default_allocator()) { allocated = aws_mem_tracer_bytes(aws_napi_get_allocator()); } AWS_NAPI_CALL(env, napi_create_int64(env, allocated, &node_allocated), { return NULL; }); return node_allocated; } napi_value aws_napi_native_memory_dump(napi_env env, napi_callback_info info) { (void)info; (void)env; if (aws_napi_get_allocator() != aws_default_allocator()) { aws_mem_tracer_dump(aws_napi_get_allocator()); } return NULL; } #if defined(_WIN32) # include static LONG WINAPI s_print_stack_trace(struct _EXCEPTION_POINTERS *exception_pointers) { aws_backtrace_print(stderr, exception_pointers); return EXCEPTION_EXECUTE_HANDLER; } #elif defined(AWS_HAVE_EXECINFO) # include static void s_print_stack_trace(int sig, siginfo_t *sig_info, void *user_data) { (void)sig; (void)sig_info; (void)user_data; aws_backtrace_print(stderr, sig_info); exit(-1); } #endif static void s_install_crash_handler(void) { #if defined(_WIN32) SetUnhandledExceptionFilter(s_print_stack_trace); #elif defined(AWS_HAVE_EXECINFO) struct sigaction sa; memset(&sa, 0, sizeof(struct sigaction)); sigemptyset(&sa.sa_mask); sa.sa_flags = SA_NODEFER; sa.sa_sigaction = s_print_stack_trace; sigaction(SIGSEGV, &sa, NULL); sigaction(SIGABRT, &sa, NULL); sigaction(SIGILL, &sa, NULL); sigaction(SIGBUS, &sa, NULL); #endif } static void s_uninstall_crash_handler(void) { #if defined(_WIN32) SetUnhandledExceptionFilter(NULL); #elif defined(AWS_HAVE_EXECINFO) sigaction(SIGSEGV, NULL, NULL); sigaction(SIGABRT, NULL, NULL); sigaction(SIGILL, NULL, NULL); sigaction(SIGBUS, NULL, NULL); #endif } static struct aws_mutex s_module_lock = AWS_MUTEX_INIT; static uint32_t s_module_initialize_count = 0; static void s_napi_context_finalize(napi_env env, void *user_data, void *finalize_hint) { (void)env; (void)finalize_hint; aws_mutex_lock(&s_module_lock); AWS_FATAL_ASSERT(s_module_initialize_count > 0); --s_module_initialize_count; if (s_module_initialize_count == 0) { aws_client_bootstrap_release(s_default_client_bootstrap); s_default_client_bootstrap = NULL; aws_host_resolver_release(s_default_host_resolver); s_default_host_resolver = NULL; aws_event_loop_group_release(s_node_uv_elg); s_node_uv_elg = NULL; aws_thread_join_all_managed(); aws_unregister_log_subject_info_list(&s_log_subject_list); aws_unregister_error_info(&s_error_list); aws_event_stream_library_clean_up(); aws_auth_library_clean_up(); aws_mqtt_library_clean_up(); s_uninstall_crash_handler(); // clean up threadsafe function lock aws_rw_lock_clean_up(&s_tsfn_lock); } struct aws_napi_context *ctx = user_data; aws_napi_logger_destroy(ctx->logger); struct aws_allocator *ctx_allocator = ctx->allocator; aws_mem_release(ctx->allocator, ctx); if (s_module_initialize_count == 0) { if (ctx_allocator != aws_default_allocator()) { if (s_allocator == ctx_allocator) { s_allocator = NULL; } aws_mem_tracer_destroy(ctx_allocator); } } aws_mutex_unlock(&s_module_lock); } static struct aws_napi_context *s_napi_context_new(struct aws_allocator *allocator, napi_env env, napi_value exports) { struct aws_napi_context *ctx = aws_mem_calloc(allocator, 1, sizeof(struct aws_napi_context)); AWS_FATAL_ASSERT(ctx && "Failed to initialize napi context"); ctx->allocator = allocator; /* bind the context to exports, thus binding its lifetime to that object */ AWS_NAPI_ENSURE(env, napi_wrap(env, exports, ctx, s_napi_context_finalize, NULL, NULL)); ctx->logger = aws_napi_logger_new(allocator, env); return ctx; } /** Helper for creating and registering a function */ static bool s_create_and_register_function( napi_env env, napi_value exports, napi_callback fn, const char *fn_name, size_t fn_name_len) { napi_value napi_fn; AWS_NAPI_CALL(env, napi_create_function(env, fn_name, fn_name_len, fn, NULL, &napi_fn), { napi_throw_error(env, NULL, "Unable to wrap native function"); return false; }); AWS_NAPI_CALL(env, napi_set_named_property(env, exports, fn_name, napi_fn), { napi_throw_error(env, NULL, "Unable to populate exports"); return false; }); return true; } #if defined(__clang__) || defined(__GNUC__) /* Suppress compiler warnings about NAPI_MODULE_INIT(). * See: https://github.com/nodejs/node/pull/49103 */ # pragma GCC diagnostic ignored "-Wstrict-prototypes" #endif /* napi_value */ NAPI_MODULE_INIT() /* (napi_env env, napi_value exports) */ { aws_mutex_lock(&s_module_lock); struct aws_allocator *allocator = aws_napi_get_allocator(); if (s_module_initialize_count == 0) { aws_rw_lock_init(&s_tsfn_lock); s_aws_enable_threadsafe_function(); s_install_crash_handler(); aws_mqtt_library_init(allocator); aws_auth_library_init(allocator); aws_event_stream_library_init(allocator); aws_register_error_info(&s_error_list); aws_register_log_subject_info_list(&s_log_subject_list); /* Initialize the event loop group */ /* * We don't currently support multi-init of the module, but we should. * Things that would need to be solved: * (1) global objects (event loop group, logger, allocator, more) * (2) multi-init/multi-cleanup of aws-c-* * (3) allocator cross-talk/lifetimes */ AWS_FATAL_ASSERT(s_node_uv_elg == NULL); s_node_uv_elg = aws_event_loop_group_new_default(allocator, 1, NULL); AWS_FATAL_ASSERT(s_node_uv_elg != NULL); /* * Default host resolver and client bootstrap to use if none specific at the javascript level. In most * cases the user doesn't even need to know about these, so let's let them leave it out completely. */ AWS_FATAL_ASSERT(s_default_host_resolver == NULL); struct aws_host_resolver_default_options resolver_options = { .max_entries = 64, .el_group = s_node_uv_elg, }; s_default_host_resolver = aws_host_resolver_new_default(allocator, &resolver_options); AWS_FATAL_ASSERT(s_default_host_resolver != NULL); AWS_FATAL_ASSERT(s_default_client_bootstrap == NULL); struct aws_client_bootstrap_options bootstrap_options = { .event_loop_group = s_node_uv_elg, .host_resolver = s_default_host_resolver, }; s_default_client_bootstrap = aws_client_bootstrap_new(allocator, &bootstrap_options); AWS_FATAL_ASSERT(s_default_client_bootstrap != NULL); } ++s_module_initialize_count; aws_mutex_unlock(&s_module_lock); /* context is bound to exports, will be cleaned up by finalizer */ s_napi_context_new(allocator, env, exports); napi_value null; napi_get_null(env, &null); #define CREATE_AND_REGISTER_FN(fn) \ if (!s_create_and_register_function(env, exports, aws_napi_##fn, #fn, sizeof(#fn))) { \ return null; \ } /* Common */ CREATE_AND_REGISTER_FN(native_memory) CREATE_AND_REGISTER_FN(native_memory_dump) CREATE_AND_REGISTER_FN(error_code_to_string) CREATE_AND_REGISTER_FN(error_code_to_name) CREATE_AND_REGISTER_FN(disable_threadsafe_function) /* IO */ CREATE_AND_REGISTER_FN(io_logging_enable) CREATE_AND_REGISTER_FN(is_alpn_available) CREATE_AND_REGISTER_FN(io_client_bootstrap_new) CREATE_AND_REGISTER_FN(io_tls_ctx_new) CREATE_AND_REGISTER_FN(io_tls_connection_options_new); CREATE_AND_REGISTER_FN(io_socket_options_new) CREATE_AND_REGISTER_FN(io_input_stream_new) CREATE_AND_REGISTER_FN(io_input_stream_append) CREATE_AND_REGISTER_FN(io_pkcs11_lib_new) CREATE_AND_REGISTER_FN(io_pkcs11_lib_close) /* MQTT Request Response */ CREATE_AND_REGISTER_FN(mqtt_request_response_client_new_from_5) CREATE_AND_REGISTER_FN(mqtt_request_response_client_new_from_311) CREATE_AND_REGISTER_FN(mqtt_request_response_client_close) CREATE_AND_REGISTER_FN(mqtt_request_response_client_submit_request) CREATE_AND_REGISTER_FN(mqtt_streaming_operation_new) CREATE_AND_REGISTER_FN(mqtt_streaming_operation_open) CREATE_AND_REGISTER_FN(mqtt_streaming_operation_close) /* MQTT5 Client */ CREATE_AND_REGISTER_FN(mqtt5_client_new) CREATE_AND_REGISTER_FN(mqtt5_client_start) CREATE_AND_REGISTER_FN(mqtt5_client_stop) CREATE_AND_REGISTER_FN(mqtt5_client_subscribe) CREATE_AND_REGISTER_FN(mqtt5_client_unsubscribe) CREATE_AND_REGISTER_FN(mqtt5_client_publish) CREATE_AND_REGISTER_FN(mqtt5_client_get_queue_statistics) CREATE_AND_REGISTER_FN(mqtt5_client_close) /* MQTT Client */ CREATE_AND_REGISTER_FN(mqtt_client_new) /* MQTT Client Connection */ CREATE_AND_REGISTER_FN(mqtt_client_connection_new) CREATE_AND_REGISTER_FN(mqtt_client_connection_connect) CREATE_AND_REGISTER_FN(mqtt_client_connection_reconnect) CREATE_AND_REGISTER_FN(mqtt_client_connection_publish) CREATE_AND_REGISTER_FN(mqtt_client_connection_subscribe) CREATE_AND_REGISTER_FN(mqtt_client_connection_on_message) CREATE_AND_REGISTER_FN(mqtt_client_connection_on_closed) CREATE_AND_REGISTER_FN(mqtt_client_connection_unsubscribe) CREATE_AND_REGISTER_FN(mqtt_client_connection_disconnect) CREATE_AND_REGISTER_FN(mqtt_client_connection_close) CREATE_AND_REGISTER_FN(mqtt_client_connection_get_queue_statistics) /* Crypto */ CREATE_AND_REGISTER_FN(hash_md5_new) CREATE_AND_REGISTER_FN(hash_sha1_new) CREATE_AND_REGISTER_FN(hash_sha256_new) CREATE_AND_REGISTER_FN(hash_update) CREATE_AND_REGISTER_FN(hash_digest) CREATE_AND_REGISTER_FN(hash_md5_compute) CREATE_AND_REGISTER_FN(hash_sha1_compute) CREATE_AND_REGISTER_FN(hash_sha256_compute) CREATE_AND_REGISTER_FN(hmac_sha256_new) CREATE_AND_REGISTER_FN(hmac_update) CREATE_AND_REGISTER_FN(hmac_digest) CREATE_AND_REGISTER_FN(hmac_sha256_compute) /* Checksums */ CREATE_AND_REGISTER_FN(checksums_crc32) CREATE_AND_REGISTER_FN(checksums_crc32c) CREATE_AND_REGISTER_FN(checksums_crc64nvme) /* HTTP */ CREATE_AND_REGISTER_FN(http_proxy_options_new) CREATE_AND_REGISTER_FN(http_connection_new) CREATE_AND_REGISTER_FN(http_connection_close) CREATE_AND_REGISTER_FN(http_stream_new) CREATE_AND_REGISTER_FN(http_stream_activate) CREATE_AND_REGISTER_FN(http_stream_close) CREATE_AND_REGISTER_FN(http_connection_manager_new) CREATE_AND_REGISTER_FN(http_connection_manager_close) CREATE_AND_REGISTER_FN(http_connection_manager_acquire) CREATE_AND_REGISTER_FN(http_connection_manager_release) /* Event stream */ CREATE_AND_REGISTER_FN(event_stream_client_connection_new) CREATE_AND_REGISTER_FN(event_stream_client_connection_connect) CREATE_AND_REGISTER_FN(event_stream_client_connection_close) CREATE_AND_REGISTER_FN(event_stream_client_connection_close_internal) CREATE_AND_REGISTER_FN(event_stream_client_connection_send_protocol_message) CREATE_AND_REGISTER_FN(event_stream_client_stream_new) CREATE_AND_REGISTER_FN(event_stream_client_stream_close) CREATE_AND_REGISTER_FN(event_stream_client_stream_activate) CREATE_AND_REGISTER_FN(event_stream_client_stream_send_message) #undef CREATE_AND_REGISTER_FN AWS_NAPI_ENSURE(env, aws_napi_http_headers_bind(env, exports)); AWS_NAPI_ENSURE(env, aws_napi_http_message_bind(env, exports)); AWS_NAPI_ENSURE(env, aws_napi_auth_bind(env, exports)); return exports; }