napi_value aws_napi_http_connection_new()

in source/http_connection.c [267:430]


napi_value aws_napi_http_connection_new(napi_env env, napi_callback_info info) {
    struct aws_allocator *allocator = aws_napi_get_allocator();

    napi_value result = NULL;
    struct aws_tls_connection_options *tls_opts = NULL;
    struct aws_http_proxy_options *proxy_opts = NULL;
    struct aws_string *host_name = NULL;
    struct aws_http_client_connection_options options = AWS_HTTP_CLIENT_CONNECTION_OPTIONS_INIT;
    options.allocator = allocator;

    /* parse/validate arguments */
    napi_value node_args[8];
    size_t num_args = AWS_ARRAY_SIZE(node_args);
    napi_value *arg = &node_args[0];
    AWS_NAPI_CALL(env, napi_get_cb_info(env, info, &num_args, node_args, NULL, NULL), {
        napi_throw_error(env, NULL, "Failed to retrieve callback information");
        return NULL;
    });
    if (num_args != AWS_ARRAY_SIZE(node_args)) {
        napi_throw_error(env, NULL, "http_connection_new needs exactly 7 arguments");
        return NULL;
    }

    napi_value node_bootstrap = *arg++;

    struct aws_client_bootstrap *bootstrap = NULL;
    struct client_bootstrap_binding *bootstrap_binding = NULL;
    napi_get_value_external(env, node_bootstrap, (void **)&bootstrap_binding);
    if (bootstrap_binding != NULL) {
        bootstrap = aws_napi_get_client_bootstrap(bootstrap_binding);
    } else {
        bootstrap = aws_napi_get_default_client_bootstrap();
    }

    /* create node external to hold the connection wrapper, cleanup is required from here on out */
    struct http_connection_binding *binding = aws_mem_calloc(allocator, 1, sizeof(struct http_connection_binding));
    if (!binding) {
        aws_napi_throw_last_error(env);
        goto alloc_failed;
    }

    binding->allocator = allocator;
    binding->env = env;

    napi_value node_on_setup = *arg++;
    if (aws_napi_is_null_or_undefined(env, node_on_setup)) {
        napi_throw_error(env, NULL, "on_connection_setup must be a callback");
        return NULL;
    }
    AWS_NAPI_CALL(
        env,
        aws_napi_create_threadsafe_function(
            env,
            node_on_setup,
            "aws_http_connection_on_connection_setup",
            s_http_on_connection_setup_call,
            binding,
            &binding->on_setup),
        { goto failed_callbacks; });

    napi_value node_on_shutdown = *arg++;
    if (!aws_napi_is_null_or_undefined(env, node_on_shutdown)) {
        AWS_NAPI_CALL(
            env,
            aws_napi_create_threadsafe_function(
                env,
                node_on_shutdown,
                "aws_http_connection_on_connection_shutdown",
                s_http_on_connection_shutdown_call,
                binding,
                &binding->on_shutdown),
            { goto failed_callbacks; });
    }

    /* will be owned by tls_options */
    napi_value node_host_name = *arg++;
    host_name = aws_string_new_from_napi(env, node_host_name);
    if (!host_name) {
        napi_throw_type_error(env, NULL, "host_name must be a String");
        goto argument_error;
    }

    napi_value node_port = *arg++;
    uint32_t port = 0;
    AWS_NAPI_CALL(env, napi_get_value_uint32(env, node_port, &port), {
        napi_throw_type_error(env, NULL, "port must be a Number");
        goto argument_error;
    });
    options.port = (uint16_t)port;

    napi_value node_socket_options = *arg++;
    AWS_NAPI_CALL(env, napi_get_value_external(env, node_socket_options, (void **)&options.socket_options), {
        napi_throw_error(env, NULL, "Unable to extract socket_options from external");
        goto argument_error;
    });

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

    napi_value node_proxy_opts = *arg++;
    if (!aws_napi_is_null_or_undefined(env, node_proxy_opts)) {
        struct http_proxy_options_binding *proxy_binding = NULL;
        AWS_NAPI_CALL(env, napi_get_value_external(env, node_proxy_opts, (void **)&proxy_binding), {
            napi_throw_error(env, NULL, "Failed to extract tls_ctx from external");
            goto argument_error;
        });
        /* proxy_options are copied internally, no need to go nuts on copies */
        proxy_opts = &proxy_binding->native;
    }

    napi_value node_external = NULL;
    AWS_NAPI_CALL(
        env, napi_create_external(env, binding, s_http_connection_binding_finalize, binding, &node_external), {
            napi_throw_error(env, NULL, "Failed to create napi external for http_connection_binding");
            goto create_external_failed;
        });

    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 create_external_failed;
    });

    options.bootstrap = bootstrap;
    options.host_name = aws_byte_cursor_from_string(host_name);
    options.on_setup = s_http_on_connection_setup;
    options.on_shutdown = s_http_on_connection_shutdown;
    options.proxy_options = proxy_opts;
    options.user_data = binding;

    if (tls_opts) {
        if (!tls_opts->server_name) {
            struct aws_byte_cursor server_name_cursor = aws_byte_cursor_from_string(host_name);
            aws_tls_connection_options_set_server_name(tls_opts, allocator, &server_name_cursor);
        }
        options.tls_options = tls_opts;
    }

    if (aws_http_client_connect(&options)) {
        aws_napi_throw_last_error(env);
        goto connect_failed;
    }

    result = node_external;
    goto done;

connect_failed:
create_external_failed:
failed_callbacks:
    if (binding) {
        AWS_NAPI_ENSURE(env, aws_napi_release_threadsafe_function(binding->on_setup, napi_tsfn_abort));
        AWS_NAPI_ENSURE(env, aws_napi_release_threadsafe_function(binding->on_shutdown, napi_tsfn_abort));
    }
    aws_mem_release(allocator, binding);
alloc_failed:
argument_error:
done:
    aws_string_destroy(host_name);
    return result;
}