napi_value aws_napi_mqtt_client_connection_new()

in source/mqtt_client_connection.c [223:459]


napi_value aws_napi_mqtt_client_connection_new(napi_env env, napi_callback_info cb_info) {

    struct aws_allocator *allocator = aws_napi_get_allocator();

    napi_value node_args[10];
    size_t num_args = AWS_ARRAY_SIZE(node_args);
    napi_value *arg = &node_args[0];
    AWS_NAPI_CALL(env, napi_get_cb_info(env, cb_info, &num_args, node_args, NULL, NULL), {
        napi_throw_error(env, NULL, "Failed to retreive callback information");
        return NULL;
    });
    if (num_args != AWS_ARRAY_SIZE(node_args)) {
        napi_throw_error(env, NULL, "mqtt_client_connection_new needs exactly 10 arguments");
        return NULL;
    }

    struct mqtt_connection_binding *binding = aws_mem_calloc(allocator, 1, sizeof(struct mqtt_connection_binding));
    AWS_FATAL_ASSERT(binding);
    binding->env = env;
    binding->allocator = allocator;

    napi_value node_external;
    AWS_NAPI_CALL(env, napi_create_external(env, binding, s_mqtt_client_connection_finalize, NULL, &node_external), {
        napi_throw_error(env, NULL, "Failed create n-api external");
        aws_mem_release(allocator, binding);
        return NULL;
    });

    /* From hereon, we need to clean up if errors occur.
     * It's good practice to store long-lived values in the binding, and clean them up from the finalizer.
     * If this new() function fails partway through, the finalizer will still run and clean them up. */

    napi_value result = NULL;

    /* Allocatations that should not outlive this function */
    struct aws_byte_buf will_topic;
    AWS_ZERO_STRUCT(will_topic);
    struct aws_byte_buf will_payload;
    AWS_ZERO_STRUCT(will_payload);
    struct aws_byte_buf username;
    AWS_ZERO_STRUCT(username);
    struct aws_byte_buf password;
    AWS_ZERO_STRUCT(password);

    napi_value node_client_external = *arg++;
    struct mqtt_nodejs_client *node_client;
    AWS_NAPI_CALL(env, napi_get_value_external(env, node_client_external, (void **)&node_client), {
        napi_throw_error(env, NULL, "Failed to extract client from external");
        goto cleanup;
    });

    napi_value node_on_interrupted = *arg++;
    if (!aws_napi_is_null_or_undefined(env, node_on_interrupted)) {
        AWS_NAPI_CALL(
            env,
            aws_napi_create_threadsafe_function(
                env,
                node_on_interrupted,
                "aws_mqtt_client_connection_on_connection_interrupted",
                s_on_connection_interrupted_call,
                binding,
                &binding->on_connection_interrupted),
            { goto cleanup; });
    }

    napi_value node_on_resumed = *arg++;
    if (!aws_napi_is_null_or_undefined(env, node_on_resumed)) {
        AWS_NAPI_CALL(
            env,
            aws_napi_create_threadsafe_function(
                env,
                node_on_resumed,
                "aws_mqtt_client_connection_on_connection_resumed",
                s_on_connection_resumed_call,
                binding,
                &binding->on_connection_resumed),
            { goto cleanup; });
    }

    /* CREATE THE THING */
    binding->connection = aws_mqtt_client_connection_new(node_client->native_client);
    if (!binding->connection) {
        napi_throw_error(env, NULL, "Failed create native connection object");
        goto cleanup;
    }

    if (binding->on_connection_interrupted || binding->on_connection_resumed) {
        aws_mqtt_client_connection_set_connection_interruption_handlers(
            binding->connection, s_on_connection_interrupted, binding, s_on_connection_resumed, binding);
    }

    napi_value node_tls = *arg++;
    if (!aws_napi_is_null_or_undefined(env, node_tls)) {
        struct aws_tls_ctx *tls_ctx;
        AWS_NAPI_CALL(env, napi_get_value_external(env, node_tls, (void **)&tls_ctx), {
            napi_throw_error(env, NULL, "Failed to extract tls_ctx from external");
            goto cleanup;
        });

        aws_tls_connection_options_init_from_ctx(&binding->tls_options, tls_ctx);
        binding->use_tls_options = true;
    }

    napi_value node_will = *arg++;
    if (!aws_napi_is_null_or_undefined(env, node_will)) {
        napi_value node_topic = NULL;
        AWS_NAPI_CALL(env, napi_get_named_property(env, node_will, "topic", &node_topic), {
            napi_throw_type_error(env, NULL, "will must contain a topic string");
            goto cleanup;
        });
        AWS_NAPI_CALL(env, aws_byte_buf_init_from_napi(&will_topic, env, node_topic), {
            aws_napi_throw_last_error(env);
            goto cleanup;
        });
        napi_value node_payload;
        AWS_NAPI_CALL(env, napi_get_named_property(env, node_will, "payload", &node_payload), {
            napi_throw_type_error(env, NULL, "will must contain a payload DataView");
            goto cleanup;
        });
        AWS_NAPI_CALL(env, aws_byte_buf_init_from_napi(&will_payload, env, node_payload), {
            aws_napi_throw_last_error(env);
            goto cleanup;
        });
        napi_value node_qos;
        AWS_NAPI_CALL(env, napi_get_named_property(env, node_will, "qos", &node_qos), {
            napi_throw_type_error(env, NULL, "will must contain a qos member");
            goto cleanup;
        });
        enum aws_mqtt_qos will_qos;
        AWS_NAPI_CALL(env, napi_get_value_int32(env, node_qos, (int32_t *)&will_qos), {
            napi_throw_type_error(env, NULL, "will.qos must be a number");
            goto cleanup;
        });
        napi_value node_retain;
        AWS_NAPI_CALL(env, napi_get_named_property(env, node_will, "retain", &node_retain), {
            napi_throw_type_error(env, NULL, "will must contain a retain member");
            goto cleanup;
        });
        bool will_retain;
        AWS_NAPI_CALL(env, napi_get_value_bool(env, node_retain, &will_retain), {
            napi_throw_type_error(env, NULL, "will.retain must be a boolean");
            goto cleanup;
        });

        struct aws_byte_cursor topic_cur = aws_byte_cursor_from_buf(&will_topic);
        struct aws_byte_cursor payload_cur = aws_byte_cursor_from_buf(&will_payload);
        if (aws_mqtt_client_connection_set_will(binding->connection, &topic_cur, will_qos, will_retain, &payload_cur)) {
            aws_napi_throw_last_error(env);
            goto cleanup;
        }
    }

    napi_value node_username = *arg++;
    if (!aws_napi_is_null_or_undefined(env, node_username)) {
        AWS_NAPI_CALL(env, aws_byte_buf_init_from_napi(&username, env, node_username), {
            napi_throw_type_error(env, NULL, "username must be a String");
            goto cleanup;
        });
    }

    napi_value node_password = *arg++;
    if (!aws_napi_is_null_or_undefined(env, node_password)) {
        AWS_NAPI_CALL(env, aws_byte_buf_init_from_napi(&password, env, node_password), {
            napi_throw_type_error(env, NULL, "password must be a String");
            goto cleanup;
        });
    }

    if (username.buffer || password.buffer) {
        struct aws_byte_cursor username_cur = aws_byte_cursor_from_buf(&username);
        struct aws_byte_cursor password_cur = aws_byte_cursor_from_buf(&password);
        if (aws_mqtt_client_connection_set_login(binding->connection, &username_cur, &password_cur)) {
            aws_napi_throw_last_error(env);
            goto cleanup;
        }
    }

    napi_value node_use_websocket = *arg++;
    bool use_websocket = false;
    if (!aws_napi_is_null_or_undefined(env, node_use_websocket)) {
        AWS_NAPI_CALL(env, napi_get_value_bool(env, node_use_websocket, &use_websocket), {
            napi_throw_type_error(env, NULL, "use_websocket must be a boolean");
            goto cleanup;
        });
    }

    napi_value node_proxy_options = *arg++;
    struct aws_http_proxy_options *proxy_options = NULL;
    if (!aws_napi_is_null_or_undefined(env, node_proxy_options)) {
        struct http_proxy_options_binding *proxy_binding = NULL;
        AWS_NAPI_CALL(env, napi_get_value_external(env, node_proxy_options, (void **)&proxy_binding), {
            napi_throw_type_error(env, NULL, "proxy_options must be an external");
            goto cleanup;
        });
        /* proxy_options are copied internally, no need to go nuts on copies */
        proxy_options = aws_napi_get_http_proxy_options(proxy_binding);
        aws_mqtt_client_connection_set_http_proxy_options(binding->connection, proxy_options);
    }

    napi_value node_transform_websocket = *arg++;
    if (use_websocket) {
        if (!aws_napi_is_null_or_undefined(env, node_transform_websocket)) {
            AWS_NAPI_CALL(
                env,
                aws_napi_create_threadsafe_function(
                    env,
                    node_transform_websocket,
                    "aws_mqtt_client_connection_transform_websocket",
                    s_transform_websocket_call,
                    binding,
                    &binding->transform_websocket),
                {
                    napi_throw_error(env, NULL, "Failed to bind transform_websocket callback");
                    goto cleanup;
                });
            aws_mqtt_client_connection_use_websockets(binding->connection, s_transform_websocket, binding, NULL, NULL);
        } else {
            aws_mqtt_client_connection_use_websockets(binding->connection, NULL, NULL, NULL, NULL);
        }
    }

    /* napi_create_reference() must be the last thing called by this function.
     * Once this succeeds, the external will not be cleaned up automatically */
    AWS_NAPI_CALL(env, napi_create_reference(env, node_external, 1, &binding->node_external), {
        napi_throw_error(env, NULL, "Failed to reference node external");
        goto cleanup;
    });

    result = node_external;

cleanup:
    aws_byte_buf_clean_up(&will_topic);
    aws_byte_buf_clean_up(&will_payload);
    aws_byte_buf_clean_up(&username);
    aws_byte_buf_clean_up(&password);
    return result;
}