6 Replies Latest reply on May 2, 2018 11:55 PM by grsr

    multiple HTTP post request threads

    vmore_3321141

      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?

        • 1. Re: multiple HTTP post request threads
          grsr

          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.

          1 of 2 people found this helpful
          • 2. Re: multiple HTTP post request threads
            vmore_3321141

            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;
                }
            }
            
            • 3. Re: multiple HTTP post request threads
              grsr

              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.

              1 of 1 people found this helpful
              • 4. Re: multiple HTTP post request threads
                vmore_3321141

                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.

                • 5. Re: multiple HTTP post request threads
                  vmore_3321141

                  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.

                  • 6. Re: multiple HTTP post request threads
                    grsr

                    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.

                    2 of 2 people found this helpful