UWS_CLIENT_HANDLE uws_client_create_with_io()

in src/uws_client.c [357:539]


UWS_CLIENT_HANDLE uws_client_create_with_io(const IO_INTERFACE_DESCRIPTION* io_interface, void* io_create_parameters, const char* hostname, unsigned int port, const char* resource_name, const WS_PROTOCOL* protocols, size_t protocol_count)
{
    UWS_CLIENT_HANDLE result;

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

        if (i < protocol_count)
        {
            /* Codes_SRS_UWS_CLIENT_01_526: [ If the protocol member of any of the items in the protocols argument is NULL, then uws_client_create_with_io shall fail and return NULL. ]*/
            LogError("Protocol index %lu has NULL name", (unsigned long)i);
            result = NULL;
        }
        else
        {
            /* Codes_SRS_UWS_CLIENT_01_515: [ uws_client_create_with_io 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_517: [ If allocating memory for the new uws instance fails then uws_client_create_with_io shall return NULL. ]*/
                LogError("Could not allocate uWS instance");
            }
            else
            {
                memset(result, 0, sizeof(UWS_CLIENT_INSTANCE));

                /* Codes_SRS_UWS_CLIENT_01_518: [ The argument hostname shall be copied for later use. ]*/
                if (mallocAndStrcpy_s(&result->hostname, hostname) != 0)
                {
                    /* Codes_SRS_UWS_CLIENT_01_519: [ 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_523: [ The argument resource_name shall be copied for later use. ]*/
                    if (mallocAndStrcpy_s(&result->resource_name, resource_name) != 0)
                    {
                        /* Codes_SRS_UWS_CLIENT_01_529: [ If allocating memory for the copy of the resource_name argument fails, then uws_client_create_with_io 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_530: [ uws_client_create_with_io 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_531: [ If singlylinkedlist_create fails then uws_client_create_with_io 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
                        {
                            /* Codes_SRS_UWS_CLIENT_01_521: [ The underlying IO shall be created by calling xio_create, while passing as arguments the io_interface and io_create_parameters argument values. ]*/
                            result->underlying_io = xio_create(io_interface, io_create_parameters);
                            if (result->underlying_io == NULL)
                            {
                                /* Codes_SRS_UWS_CLIENT_01_522: [ If xio_create fails, then uws_client_create_with_io shall fail and return NULL. ]*/
                                LogError("Cannot create underlying IO.");
                                singlylinkedlist_destroy(result->pending_sends);
                                Map_Destroy(result->request_headers);
                                free(result->resource_name);
                                free(result->hostname);
                                free(result);
                                result = NULL;
                            }
                            else
                            {
                                // Set the underlying socket to turn on renegotiation
                                bool set_renegotiation = true;
                                (void)xio_setoption(result->underlying_io, OPTION_SET_TLS_RENEGOTIATION, &set_renegotiation);

                                result->uws_state = UWS_STATE_CLOSED;

                                /* Codes_SRS_UWS_CLIENT_01_520: [ 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_524: [ 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_527: [ 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_528: [ If allocating memory for the copied protocol information fails then uws_client_create_with_io 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;
}