multiple HTTP post request threads

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
agmi_3321141
Level 4
Level 4
5 likes given First like received First like given

Hello All!

I have a http post request thread that pops from a queue and posts its contents to a server. I notice that my queue gets full pretty quickly and I start losing the packets to be sent, or have increased latency. Increasing my queue size will not help with the latency part as that depends on how quickly the send thread is able to process the message. Can I create multiple threads that make the HTTP post request? Is that a good idea or should I look at speeding up my existing thread?

Another question I had is how to disable forever ble_observe. Using 0 as an argument to the function puts it into forever scan mode, how can I stop it?

0 Likes
1 Solution

The http_client_connect() and http_client_configure() should be called once. In addition, remove http_request_deinit() call. I could loop multiple HTTP requests with these changes.

View solution in original post

6 Replies
GauravS_31
Moderator
Moderator
Moderator
10 questions asked 250 solutions authored 250 sign-ins

You should consider speeding up the processing time of the send thread and HTTP response callback thread such that the rate at which the queue is filled up (push) is less than the queue processing rate (pop). You can use wiced_rtos_get_semaphore() after sending the HTTP request and use wiced_rtos_set_semaphore() after receiving the response. To disable ble_observe, you can call the function wiced_bt_ble_observe() with first argument as FALSE.

Thanks for the inputs. I monitored the push and pop rates per minute. I maintain a queue of size 300 and I can see that my push calls are consistently higher(~125) than the pop calls(~100). This leads to my queue to eventually get full over a period of few minutes. All my pop call result in successful HTTP post requests. I'm averaging about 100 post requests per minute. The pop call is followed by a send_packet() call that packs the message and make the pot request. Here is the structure of the thread in the code below. I am trying to find bottlenecks in the code, possibly sprintf. But I tried using memcpy to just copy memory chunks to creat ethe message but that didn't help greatly either. Is there something

i can do to speed things up? Is it advisabll to create multiple threads that do the post request? I ran malloc_info and these were the stats when the app was running. It seems high, but I am not sure what to expect, so may be you can tell me if this is expected or too high.

malloc_info {

        arena:          64356;  /* total space allocated from system */

        ordblks:            5;  /* number of non-inuse chunks */

        smblks:             0;  /* unused -- always zero */

        hblks:              0;  /* number of mmapped regions */

        hblkhd:             0;  /* total space in mmapped regions */

        usmblks:            0;  /* unused -- always zero */

        fsmblks:            0;  /* unused -- always zero */

        uordblks:       57228;  /* total allocated space */

        fordblks:        7128;  /* total non-inuse space */

        keepcost:        6856;  /* top-most, releasable (via malloc_trim) space */

};

******Mallinfo summary******

Total heap size: 64356 bytes

Memory in use: 57228 bytes

static void http_send_thread( uint32_t arg )

{

    wiced_result_t result;

    WPRINT_APP_INFO (( "Started httpservice_thread\n" ));

    do

    {

        /*wait for network up*/

        wiced_rtos_delay_milliseconds( 100 );

    } while ( wiced_network_is_ip_up( WICED_STA_INTERFACE ) != WICED_TRUE );

    while ( 1 )

    {

        /* check for network link */

        if ( wiced_network_is_ip_up( WICED_STA_INTERFACE ) != WICED_TRUE )

        {

            wiced_rtos_delay_milliseconds( 100 );

            continue;

        }

        http_q pop_q;

        result = wiced_rtos_pop_from_queue( &my_http_queue, &pop_q, WICED_NEVER_TIMEOUT );

        send_packet(&pop_q);

    }

}

static void send_packet(http_q *packet)

{

    wiced_result_t result;

    WPRINT_APP_DEBUG( ( "Connecting to %s\n", SERVER_HOST ) );

    // configure HTTP client parameters

    client_configuration.flag = (http_client_configuration_flags_t)(HTTP_CLIENT_CONFIG_FLAG_SERVER_NAME | HTTP_CLIENT_CONFIG_FLAG_MAX_FRAGMENT_LEN);

    client_configuration.server_name = (uint8_t*) SERVER_HOST;

    client_configuration.max_fragment_length = TLS_FRAGMENT_LENGTH_1024;

    http_client_configure(&client, &client_configuration);

    // if you set hostname, library will make sure subject name in the server certificate is matching with host name you are trying to connect. pass NULL if you don't want to enable this check

    client.peer_cn = (uint8_t*) SERVER_HOST;

    if ( ( result = http_client_connect( &client, (const wiced_ip_address_t*)&ip_address, SERVER_PORT, HTTP_NO_SECURITY, CONNECT_TIMEOUT_MS ) ) != WICED_SUCCESS )

    {

        WPRINT_APP_INFO( ( "Error: failed to connect to server: %u\n", result) );

        return;

    }

    WPRINT_APP_DEBUG( ( "Connected\n" ) );

    http_header_field_t header[3];

    char messageBody[200];        // Enough to hold the message body

    char messageLengthBuffer[10]; // Enough to hold the characters for the Content-Length: header

    char payload[25];

    uint8_t *p = (packet->data);

    base64_encode((unsigned char*)p,13,(unsigned char*)payload,25,BASE64_STANDARD);

    wiced_iso8601_time_t curr_time;

    wiced_time_get_iso8601_time(&curr_time);

    char time[sizeof(wiced_iso8601_time_t) + 1];

    memcpy(&time, &curr_time, sizeof(time));

    time[sizeof(wiced_iso8601_time_t)] = '\0';

    sprintf(messageBody,"{\n\"payload\": \"%s\",\n\"receiverAddress\": \"%02X:%02X:%02X:%02X:%02X:%02X\",\n\"rssi\": %d,\n\"senderAddress\": \"%02X:%02X:%02X:%02X:%02X:%02X\",\n\"time\": \"%s\"\n}",

        payload,

        local_bda[0],

        local_bda[1],

        local_bda[2],

        local_bda[3],

        local_bda[4],

        local_bda[5],

        packet->rssi[0],

        packet->remote_bda[0],

        packet->remote_bda[1],

        packet->remote_bda[2],

        packet->remote_bda[3],

        packet->remote_bda[4],

        packet->remote_bda[5],

        time);

    header[0].field        = HTTP_HEADER_HOST;

    header[0].field_length = sizeof( HTTP_HEADER_HOST ) - 1;

    header[0].value        = SERVER_HOST;

    header[0].value_length = sizeof( SERVER_HOST ) - 1;

    #define JSON_URL "application/json"

    header[1].field        = HTTP_HEADER_CONTENT_TYPE;

    header[1].field_length = sizeof( HTTP_HEADER_CONTENT_TYPE ) - 1;

    header[1].value        = JSON_URL;

    header[1].value_length = sizeof( JSON_URL ) - 1;

    sprintf(messageLengthBuffer," %d",strlen(messageBody)); // Put message body into the buffer so that you can strlen it

    header[2].field        = HTTP_HEADER_CONTENT_LENGTH;

    header[2].field_length = sizeof( HTTP_HEADER_CONTENT_LENGTH ) - 1;

    header[2].value        = messageLengthBuffer;

    header[2].value_length = strlen(messageLengthBuffer);

    http_request_init( &requests[0], &client, HTTP_POST, request_uris[0], HTTP_1_1 );

    http_request_write_header( &requests[0], &header[0], 3 );

    http_request_write_end_header( &requests[0] );

    http_request_write( &requests[0], (uint8_t*)messageBody, strlen(messageBody) );

    result = http_request_flush( &requests[0] );

    if ( result != WICED_SUCCESS )

    {

        WPRINT_APP_INFO( (" POST request flush failed\n") );

    }

    else

    {

        result = wiced_rtos_get_semaphore(&requestSemaphore,5000);

        if ( result != WICED_SUCCESS )

            WPRINT_APP_INFO( (" POST request timed out \n") );

        else

        {

            WPRINT_APP_DEBUG( ("\nPOST successful\n") );

        }

    }

    http_request_deinit( &requests[0] );

    http_client_disconnect( &client );

}

static void event_handler( http_client_t* client, http_event_t event, http_response_t* response )

{

    switch( event )

    {

        case HTTP_DATA_RECEIVED:

        {

            // set semaphore

            wiced_rtos_set_semaphore(&requestSemaphore);

        break;

        }

        default:

        break;

    }

}

0 Likes

The http_client_connect() could cause bottleneck because TLS handshakes would take time. Use "Connection: keep-alive" header and check if the HTTP connection persists. If yes, then you can consider connecting only once and then sending HTTP requests. This should speed up the pop queue rate. Debug printing could also be a bottleneck so you could try to minimize its use. A single HTTP thread should be sufficient to send multiple HTTP requests if the queue is rapidly processed. Creating multiple HTTP threads will complicate the design and will not be a scalable solution according to me. Regarding the mallinfo, I'm not sure where you have called it. It will be better to call it twice- before and after a function call so that the relative memory consumption could be calculated.

Thanks, http_client_connect() does seem to be a bottleneck. I tried to time it and I see that I easily spend half the time per minute initiating connections, so keep-alive would be really useful. I added a keep alive header and got rid of the disconnect call, but after getting rid of the disconnect call, I am unable to connect to the server. I continually see the error message:

Error: failed to connect to server: 4

My send_packet code now looks like this, now with additional headers and no disconnection call:

static void send_packet(http_q *packet) 

    wiced_result_t result; 

    WPRINT_APP_DEBUG( ( "Connecting to %s\n", SERVER_HOST ) ); 

 

    // configure HTTP client parameters 

    client_configuration.flag = (http_client_configuration_flags_t)(HTTP_CLIENT_CONFIG_FLAG_SERVER_NAME | HTTP_CLIENT_CONFIG_FLAG_MAX_FRAGMENT_LEN); 

    client_configuration.server_name = (uint8_t*) SERVER_HOST; 

    client_configuration.max_fragment_length = TLS_FRAGMENT_LENGTH_1024; 

    http_client_configure(&client, &client_configuration); 

 

    // if you set hostname, library will make sure subject name in the server certificate is matching with host name you are trying to connect. pass NULL if you don't want to enable this check 

    client.peer_cn = (uint8_t*) SERVER_HOST; 

 

    if ( ( result = http_client_connect( &client, (const wiced_ip_address_t*)&ip_address, SERVER_PORT, HTTP_NO_SECURITY, CONNECT_TIMEOUT_MS ) ) != WICED_SUCCESS ) 

    { 

        WPRINT_APP_INFO( ( "Error: failed to connect to server: %u\n", result) ); 

        return; 

    } 

 

    WPRINT_APP_DEBUG( ( "Connected\n" ) ); 

 

    http_header_field_t header[5]; 

    char messageBody[200];        // Enough to hold the message body 

    char messageLengthBuffer[10]; // Enough to hold the characters for the Content-Length: header 

    char payload[25]; 

    uint8_t *p = (packet->data); 

    base64_encode((unsigned char*)p,13,(unsigned char*)payload,25,BASE64_STANDARD); 

 

    wiced_iso8601_time_t curr_time; 

    wiced_time_get_iso8601_time(&curr_time); 

    char time[sizeof(wiced_iso8601_time_t) + 1]; 

    memcpy(&time, &curr_time, sizeof(time)); 

    time[sizeof(wiced_iso8601_time_t)] = '\0'; 

 

    sprintf(messageBody,"{\n\"payload\": \"%s\",\n\"receiverAddress\": \"%02X:%02X:%02X:%02X:%02X:%02X\",\n\"rssi\": %d,\n\"senderAddress\": \"%02X:%02X:%02X:%02X:%02X:%02X\",\n\"time\": \"%s\"\n}", 

        payload, 

        local_bda[0], 

        local_bda[1], 

        local_bda[2], 

        local_bda[3], 

        local_bda[4], 

        local_bda[5], 

        packet->rssi[0], 

        packet->remote_bda[0], 

        packet->remote_bda[1], 

        packet->remote_bda[2], 

        packet->remote_bda[3], 

        packet->remote_bda[4], 

        packet->remote_bda[5], 

        time); 

 

    header[0].field        = HTTP_HEADER_HOST; 

    header[0].field_length = sizeof( HTTP_HEADER_HOST ) - 1; 

    header[0].value        = SERVER_HOST; 

    header[0].value_length = sizeof( SERVER_HOST ) - 1; 

 

    #define JSON_URL "application/json" 

    header[1].field        = HTTP_HEADER_CONTENT_TYPE; 

    header[1].field_length = sizeof( HTTP_HEADER_CONTENT_TYPE ) - 1; 

    header[1].value        = JSON_URL; 

    header[1].value_length = sizeof( JSON_URL ) - 1; 

 

    sprintf(messageLengthBuffer," %d",strlen(messageBody)); // Put message body into the buffer so that you can strlen it 

    header[2].field        = HTTP_HEADER_CONTENT_LENGTH; 

    header[2].field_length = sizeof( HTTP_HEADER_CONTENT_LENGTH ) - 1; 

    header[2].value        = messageLengthBuffer; 

    header[2].value_length = strlen(messageLengthBuffer); 

 

    #define CONNECTION "Keep-Alive"

    #define HTTP_HEADER_CONNECTION "Connection: "

    header[3].field        = HTTP_HEADER_CONNECTION;

    header[3].field_length = sizeof( HTTP_HEADER_CONNECTION ) - 1;

    header[3].value        = CONNECTION;

    header[3].value_length = sizeof(CONNECTION) - 1;

 

    #define ALIVE "timeout=5, max=100"

    #define HTTP_HEADER_KEEP_ALIVE "Keep-Alive: "

    header[4].field        = HTTP_HEADER_KEEP_ALIVE;

    header[4].field_length = sizeof( HTTP_HEADER_KEEP_ALIVE ) - 1;

    header[4].value        = ALIVE;

    header[4].value_length = sizeof(ALIVE) - 1;

 

    http_request_init( &requests[0], &client, HTTP_POST, request_uris[0], HTTP_1_1 ); 

    http_request_write_header( &requests[0], &header[0], 5 ); 

    http_request_write_end_header( &requests[0] ); 

    http_request_write( &requests[0], (uint8_t*)messageBody, strlen(messageBody) ); 

    result = http_request_flush( &requests[0] ); 

 

    if ( result != WICED_SUCCESS ) 

    { 

        WPRINT_APP_INFO( (" POST request flush failed\n") ); 

    } 

    else 

    { 

        result = wiced_rtos_get_semaphore(&requestSemaphore,5000); 

 

        if ( result != WICED_SUCCESS ) 

            WPRINT_APP_INFO( (" POST request timed out \n") ); 

        else 

        { 

            WPRINT_APP_DEBUG( ("\nPOST successful\n") ); 

        } 

    } 

    http_request_deinit( &requests[0] ); 

What is the right way to test the keep alive connection without a disconnection call. Should the connection just be called once? If I replace the http connection call to happen just once, something like this:

if ( wiced_network_is_ip_up( WICED_STA_INTERFACE ) == WICED_TRUE && is_connected_http == WICED_FALSE) {

    if ( ( result = http_client_connect( &client, (const wiced_ip_address_t*)&ip_address, SERVER_PORT, HTTP_NO_SECURITY, CONNECT_TIMEOUT_MS ) ) != WICED_SUCCESS )

    {

        WPRINT_APP_INFO( ( "Error: failed to connect to server: %u\n", result) );

        return;

    } else {

        is_connected_http = WICED_TRUE;

    }

}

Then I get a POST timeout. Hope you can tell me how to do this correctly.

PS: For sanity test to see everything is okay on the server side of things, I used the requests python package which has an API called session that can hold the HTTP connection open. If i make a bunch of connections using that API , I can see that HTTP connection is not re-opened for every POST request.

0 Likes

Hello, bumping this!

It'd be great if someone can clarify how to correctly use the http keep alive connection as a client.

I have been able to make a keep-alive post request using a simple python client to my local server, but haven't succeeded doing so with the WICED device. I am using the code outlined in my last post.

0 Likes

The http_client_connect() and http_client_configure() should be called once. In addition, remove http_request_deinit() call. I could loop multiple HTTP requests with these changes.