UWS_CLIENT_HANDLE uws_client_create()

in src/uws_client.c [110:355]


UWS_CLIENT_HANDLE uws_client_create(const char* hostname, unsigned int port, const char* resource_name, bool use_ssl, const WS_PROTOCOL* protocols, size_t protocol_count)
{
    UWS_CLIENT_HANDLE result;

    /* Codes_SRS_UWS_CLIENT_01_002: [ If any of the arguments hostname and resource_name is NULL then uws_client_create shall return NULL. ]*/
    if ((hostname == NULL) ||
        (resource_name == NULL) ||
        /* Codes_SRS_UWS_CLIENT_01_411: [ If protocol_count is non zero and protocols is NULL then uws_client_create shall fail and return NULL. ]*/
        ((protocols == NULL) && (protocol_count > 0)))
    {
        LogError("Invalid arguments: hostname = %p, resource_name = %p, protocols = %p, protocol_count = %lu", hostname, resource_name, protocols, (unsigned long)protocol_count);
        result = NULL;
    }
    else
    {
        /* Codes_SRS_UWS_CLIENT_01_412: [ If the protocol member of any of the items in the protocols argument is NULL, then uws_client_create shall fail and return NULL. ]*/
        size_t i;
        for (i = 0; i < protocol_count; i++)
        {
            if (protocols[i].protocol == NULL)
            {
                break;
            }
        }

        if (i < protocol_count)
        {
            LogError("Protocol index %lu has NULL name", (unsigned long)i);
            result = NULL;
        }
        else
        {
            /* Codes_SRS_UWS_CLIENT_01_001: [uws_client_create shall create an instance of uws and return a non-NULL handle to it.]*/
            result = (UWS_CLIENT_HANDLE)malloc(sizeof(UWS_CLIENT_INSTANCE));
            if (result == NULL)
            {
                /* Codes_SRS_UWS_CLIENT_01_003: [ If allocating memory for the new uws instance fails then uws_client_create shall return NULL. ]*/
                LogError("Could not allocate uWS instance");
            }
            else
            {
                (void)memset(result, 0, sizeof(UWS_CLIENT_INSTANCE));

                /* Codes_SRS_UWS_CLIENT_01_004: [ The argument hostname shall be copied for later use. ]*/
                if (mallocAndStrcpy_s(&result->hostname, hostname) != 0)
                {
                    /* Codes_SRS_UWS_CLIENT_01_392: [ If allocating memory for the copy of the hostname argument fails, then uws_client_create shall return NULL. ]*/
                    LogError("Could not copy hostname.");
                    free(result);
                    result = NULL;
                }
                else
                {
                    /* Codes_SRS_UWS_CLIENT_01_404: [ The argument resource_name shall be copied for later use. ]*/
                    if (mallocAndStrcpy_s(&result->resource_name, resource_name) != 0)
                    {
                        /* Codes_SRS_UWS_CLIENT_01_405: [ If allocating memory for the copy of the resource argument fails, then uws_client_create shall return NULL. ]*/
                        LogError("Could not copy resource.");
                        free(result->hostname);
                        free(result);
                        result = NULL;
                    }
                    else if ((result->request_headers = Map_Create(NULL)) == NULL)
                    {
                        LogError("Failed allocating MAP for request headers");
                        free(result->resource_name);
                        free(result->hostname);
                        free(result);
                        result = NULL;
                    }
                    else
                    {
                        /* Codes_SRS_UWS_CLIENT_01_017: [ uws_client_create shall create a pending send IO list that is to be used to queue send packets by calling singlylinkedlist_create. ]*/
                        result->pending_sends = singlylinkedlist_create();
                        if (result->pending_sends == NULL)
                        {
                            /* Codes_SRS_UWS_CLIENT_01_018: [ If singlylinkedlist_create fails then uws_client_create shall fail and return NULL. ]*/
                            LogError("Could not allocate pending send frames list");
                            Map_Destroy(result->request_headers);
                            free(result->resource_name);
                            free(result->hostname);
                            free(result);
                            result = NULL;
                        }
                        else
                        {
                            if (use_ssl == true)
                            {
                                TLSIO_CONFIG tlsio_config;

                                /* Codes_SRS_UWS_CLIENT_01_006: [ If use_ssl is true then uws_client_create shall obtain the interface used to create a tlsio instance by calling platform_get_default_tlsio. ]*/
                                /* Codes_SRS_UWS_CLIENT_01_076: [ If /secure/ is true, the client MUST perform a TLS handshake over the connection after opening the connection and before sending the handshake data [RFC2818]. ]*/
                                const IO_INTERFACE_DESCRIPTION* tlsio_interface = platform_get_default_tlsio();
                                if (tlsio_interface == NULL)
                                {
                                    /* Codes_SRS_UWS_CLIENT_01_007: [ If obtaining the underlying IO interface fails, then uws_client_create shall fail and return NULL. ]*/
                                    LogError("NULL TLSIO interface description");
                                    result->underlying_io = NULL;
                                }
                                else
                                {
                                    SOCKETIO_CONFIG socketio_config;

                                    /* Codes_SRS_UWS_CLIENT_01_013: [ The create arguments for the tls IO (when use_ssl is 1) shall have: ]*/
                                    /* Codes_SRS_UWS_CLIENT_01_014: [ - hostname set to the hostname argument passed to uws_client_create. ]*/
                                    /* Codes_SRS_UWS_CLIENT_01_015: [ - port set to the port argument passed to uws_client_create. ]*/
                                    socketio_config.hostname = hostname;
                                    socketio_config.port = port;
                                    socketio_config.accepted_socket = NULL;

                                    tlsio_config.hostname = hostname;
                                    tlsio_config.port = port;
                                    tlsio_config.underlying_io_interface = socketio_get_interface_description();
                                    tlsio_config.underlying_io_parameters = &socketio_config;

                                    result->underlying_io = xio_create(tlsio_interface, &tlsio_config);
                                    if (result->underlying_io == NULL)
                                    {
                                        LogError("Cannot create underlying TLS IO.");
                                    }
                                    else
                                    {
                                        // Set the underlying socket to turn on renegotiation
                                        bool set_renegotiation = true;
                                        xio_setoption(result->underlying_io, OPTION_SET_TLS_RENEGOTIATION, &set_renegotiation);
                                    }
                                }
                            }
                            else
                            {
                                SOCKETIO_CONFIG socketio_config;
                                /* Codes_SRS_UWS_CLIENT_01_005: [ If use_ssl is false then uws_client_create shall obtain the interface used to create a socketio instance by calling socketio_get_interface_description. ]*/
                                const IO_INTERFACE_DESCRIPTION* socketio_interface = socketio_get_interface_description();
                                if (socketio_interface == NULL)
                                {
                                    /* Codes_SRS_UWS_CLIENT_01_007: [ If obtaining the underlying IO interface fails, then uws_client_create shall fail and return NULL. ]*/
                                    LogError("NULL socketio interface description");
                                    result->underlying_io = NULL;
                                }
                                else
                                {
                                    /* Codes_SRS_UWS_CLIENT_01_010: [ The create arguments for the socket IO (when use_ssl is 0) shall have: ]*/
                                    /* Codes_SRS_UWS_CLIENT_01_011: [ - hostname set to the hostname argument passed to uws_client_create. ]*/
                                    /* Codes_SRS_UWS_CLIENT_01_012: [ - port set to the port argument passed to uws_client_create. ]*/
                                    socketio_config.hostname = hostname;
                                    socketio_config.port = port;
                                    socketio_config.accepted_socket = NULL;

                                    /* Codes_SRS_UWS_CLIENT_01_008: [ The obtained interface shall be used to create the IO used as underlying IO by the newly created uws instance. ]*/
                                    /* Codes_SRS_UWS_CLIENT_01_009: [ The underlying IO shall be created by calling xio_create. ]*/
                                    result->underlying_io = xio_create(socketio_interface, &socketio_config);
                                    if (result->underlying_io == NULL)
                                    {
                                        LogError("Cannot create underlying socket IO.");
                                    }
                                }
                            }

                            if (result->underlying_io == NULL)
                            {
                                /* Codes_SRS_UWS_CLIENT_01_016: [ If xio_create fails, then uws_client_create shall fail and return NULL. ]*/
                                singlylinkedlist_destroy(result->pending_sends);
                                Map_Destroy(result->request_headers);
                                free(result->resource_name);
                                free(result->hostname);
                                free(result);
                                result = NULL;
                            }
                            else
                            {
                                result->uws_state = UWS_STATE_CLOSED;
                                /* Codes_SRS_UWS_CLIENT_01_403: [ The argument port shall be copied for later use. ]*/
                                result->port = port;

                                result->fragmented_frame_type = WS_FRAME_TYPE_UNKNOWN;

                                result->protocol_count = protocol_count;

                                /* Codes_SRS_UWS_CLIENT_01_410: [ The protocols argument shall be allowed to be NULL, in which case no protocol is to be specified by the client in the upgrade request. ]*/
                                if (protocols == NULL)
                                {
                                    result->protocols = NULL;
                                }
                                else
                                {
                                    size_t malloc_size = safe_multiply_size_t(sizeof(WS_INSTANCE_PROTOCOL), protocol_count);
                                    if (malloc_size == SIZE_MAX ||
                                        (result->protocols = (WS_INSTANCE_PROTOCOL*)malloc(malloc_size)) == NULL)
                                    {
                                        /* Codes_SRS_UWS_CLIENT_01_414: [ If allocating memory for the copied protocol information fails then uws_client_create shall fail and return NULL. ]*/
                                        LogError("Cannot allocate memory for the protocols array. size=%zu", malloc_size);
                                        xio_destroy(result->underlying_io);
                                        singlylinkedlist_destroy(result->pending_sends);
                                        Map_Destroy(result->request_headers);
                                        free(result->resource_name);
                                        free(result->hostname);
                                        free(result);
                                        result = NULL;
                                    }
                                    else
                                    {
                                        /* Codes_SRS_UWS_CLIENT_01_413: [ The protocol information indicated by protocols and protocol_count shall be copied for later use (for constructing the upgrade request). ]*/
                                        for (i = 0; i < protocol_count; i++)
                                        {
                                            if (mallocAndStrcpy_s(&result->protocols[i].protocol, protocols[i].protocol) != 0)
                                            {
                                                /* Codes_SRS_UWS_CLIENT_01_414: [ If allocating memory for the copied protocol information fails then uws_client_create shall fail and return NULL. ]*/
                                                LogError("Cannot allocate memory for the protocol index %u.", (unsigned int)i);
                                                break;
                                            }
                                        }

                                        if (i < protocol_count)
                                        {
                                            size_t j;

                                            for (j = 0; j < i; j++)
                                            {
                                                free(result->protocols[j].protocol);
                                            }

                                            free(result->protocols);
                                            xio_destroy(result->underlying_io);
                                            singlylinkedlist_destroy(result->pending_sends);
                                            Map_Destroy(result->request_headers);
                                            free(result->resource_name);
                                            free(result->hostname);
                                            free(result);
                                            result = NULL;
                                        }
                                        else
                                        {
                                            result->protocol_count = protocol_count;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    return result;
}