Quality RTOS & Embedded Software

  Real time embedded FreeRTOS RSS feed  
NOTE: The HTTPS library and documentation are in the FreeRTOS Labs.  The libraries in the FreeRTOS Labs download directory are fully functional, but undergoing optimizations or refactoring to improve memory usage, modularity, documentation, demo usability, or test coverage.  They are available as part of the main download.

Basic HTTP Client Demo (without TLS and in plaintext)

Notice:  It is our recommendation to always use strong mutual authentication in any Internet of Things (IoT) application. The first project (this page) is only provided to validate HTTP communication can be established prior to introducing encryption and authentication, and to allow the HTTP packets to be observed using a network packet sniffer such as Wireshark for those who wish to do so. This project is not intended to be an example suitable for production use.

 

Introduction

The basic HTTP Client demo project uses the FreeRTOS Windows port, enabling it to be built and evaluated with the free Community version of Visual Studios on Windows, so without the need for any particular MCU hardware.

The example projects documented on these pages introduce the concepts described in the “TLS Introduction” section one at a time. The first example (this page) demonstrates unencrypted HTTP communication, the second example builds on the first to introduce weak server authentication, and the third example builds on the second to introduce strong mutual authentication.

The basic HTTP Client demo is intended to showcase only the basic HTTP use cases of connecting to an HTTP server and sending a few requests.  It uses the simpler synchronous HTTP library API and does not create a secure connection. The basic HTTP Client connects to a specific HTTP test server supporting GET, HEAD, PUT, and POST requests. The instructions provided below will demonstrate how to connect to either the httpbin.org server hosted on the internet or on a server running locally on the host.

This demo is intended to be used as a learning exercise only.  All HTTP messages are sent in plaintext and are not encrypted.  Do NOT send any confidential information from your device to the HTTP server.  The HTTP server is publicly hosted by a 3rd party that is not affiliated with FreeRTOS.  This HTTP server may be unavailable at any time, and it is not maintained by FreeRTOS.  

Note: http://httpbin.org is an open source HTTP test server that supports HTTP/1.1.  You can find more information at https://github.com/postmanlabs/httpbin.

Source Code Organization


Click to enlarge

The Visual Studio solution for the basic HTTP demo is called http_plain_text_demo.sln and is located in the \FreeRTOS-Plus\Demo\FreeRTOS_IoT_Libraries\https\http_plain_text directory of the main FreeRTOS download.

Note: The project is part of the main FreeRTOS download.

The IoT HTTPS Client library utilizes the IoT Task Pool library, and the HTTP Client demo project was created by adding the IoT HTTPS Client library source files shown in the image below into task pool demo project.

 

Configuring the Demo Project

The demo uses the FreeRTOS+TCP TCP/IP stack, so follow the instructions provided for the TCP/IP starter project to ensure you:

  1. Have the pre-requisite components installed (such as WinPCap).
  2. Optionally set a static or dynamic IP address, gateway address and netmask.
  3. Optionally set a MAC address.
  4. Select an Ethernet network interface on your host machine.
  5. …and importantly test your network connection before attempting to run the HTTP Client demo.

All these setting should be performed in the HTTP Client demo project, not the TCP/IP starter project referenced from the same page! As delivered the TCP/IP stack is configured to use a dynamic IP address.

Configuring the HTTP Server Connection

HTTP Server (Web Hosted)

The demo project is pre-configured to communicate with the publicly hosted HTTP server at “httpbin.org” – so the network to which the demo is connected must have a DHCP service and internet access. 

Note: Public HTTP servers may be slow to respond

If you would like to connect to a different public server then:

  1. Open FreeRTOS-Plus\Demo\FreeRTOS_IoT_Libraries\include\https_demo_profile.h.
  2. Edit the following lines to be correct for your chosen server:
    #define httpsdemoprofileSERVER_ADDRESS "httpbin.org"
    #define httpsdemoprofileSERVER_PORT 80
  3. Open FreeRTOS-Plus\Demo\FreeRTOS_IoT_Libraries\https\common\DemoTasks\SimpleHTTPSExamples.c
  4. Update the following lines for the correct path to perform these methods. Note: you can remove the #define for a particular method if the new server does not support a particular API.
    #define httpsexampleHTTPS_GET_PATH "/ip"
    #define httpsexampleHTTPS_HEAD_PATH "/ip"
    #define httpsexampleHTTPS_PUT_PATH "/put"
    #define httpsexampleHTTPS_POST_PATH "/post"

HTTP Server (Host Machine)

It is also possible to run the HTTP Server locally, either on your host machine (the machine used to build the demo application), or another machine on your local network.  To do this:

  1. You will need to have Docker installed and setup on the machine.
  2. Follow the instructions on https://github.com/postmanlabs/httpbin to run httpbin in a docker container.
  3. Find the IP address of your host machine (run the ipconfig command on Windows, or ifconfig on Linux or MAC OS).
  4. Open FreeRTOS-Plus\Demo\FreeRTOS_IoT_Libraries\include\https_demo_profile.h.
  5. Edit the following lines so httpsdemoprofileSERVER_ADDRESS is set to the IP address of the machine on which httpbin is running.  

#define httpsdemoprofileSERVER_ADDRESS "httpbin.org"
#define httpsdemoprofileSERVER_PORT 80

Note: Port number 80 is the default port number for unencrypted HTTP.  If you cannot use that port (for example if it is blocked by your IT security policy) then change the port used by httpbin to a high port number (for example something in the 50000 to 55000 range), and set httpexampleHTTP_SERVER_PORT accordingly.

 

Building the Demo Project

The demo project uses the free community edition of Visual Studio

To build the demo:

  1. Open the \FreeRTOS-Plus\Demo\FreeRTOS_IoT_Libraries\https\http_plain_text\http_plain_text.sln Visual Studio solution file from within the Visual Studio IDE
  2. Select ‘build solution’ from the IDE’s ‘build’ menu

Functionality

The demo creates a single application task that loops through a set of examples that demonstrate how to connect to the server, send a few HTTP requests, and disconnect from the server again. The demo application sends synchronously sends four requests to the server: GET, HEAD, PUT, and POST. The structure of the demo is shown below:

static void prvHTTPSDemoTask( void * pvParameters )
{
  /* Remove compiler warnings about unused parameters. */
  ( void )pvParameters;

  /* One time initialisation of the libraries used by this demo. */
  prvInitialiseLibraries();

  for( ; ; )
  {
    /****************************** Connect. ******************************/

    /* Establish a connection to the HTTPS server. This example connects to
     * the HTTPS server as specified in httpsexampleHTTPS_SERVER_ADDRESS and
     * httpsexampleHTTPS_SERVER_PORT at the top of this file. Please change
     * it to the HTTPS server you want to connect to. */
    configPRINTF( ( "Attempt to connect to %s:%d\r\n", httpsexampleHTTPS_SERVER_ADDRESS, httpsexampleHTTPS_SERVER_PORT ) );
    prvHTTPSConnect();
    configPRINTF( ( "Connected to %s:%d\r\n", httpsexampleHTTPS_SERVER_ADDRESS, httpsexampleHTTPS_SERVER_PORT ) );

    /*********************** Send HTTPS requests. ************************/

    /* The client is now connected to the server. This example will send a
     * GET, HEAD, PUT, and POST request. For AWS IoT profile, the example will
     * only send a POST request.
     **/
    configPRINTF( ( "Sending HTTPS GET request...\r\n" ) );
    prvHTTPSRequest( IOT_HTTPS_METHOD_GET,
                     httpsexampleHTTPS_GET_PATH,
                     sizeof( httpsexampleHTTPS_GET_PATH ) - 1 );

    configPRINTF( ( "Sending HTTPS HEAD request...\r\n" ) );
    prvHTTPSRequest( IOT_HTTPS_METHOD_HEAD,
                     httpsexampleHTTPS_HEAD_PATH,
                     sizeof( httpsexampleHTTPS_HEAD_PATH ) - 1 );

    configPRINTF( ( "Sending HTTPS PUT request...\r\n" ) );
    prvHTTPSRequest( IOT_HTTPS_METHOD_PUT,
                     httpsexampleHTTPS_PUT_PATH,
                     sizeof( httpsexampleHTTPS_PUT_PATH ) - 1 );

    configPRINTF( ( "Sending HTTPS POST request...\r\n" ) );
    prvHTTPSRequest( IOT_HTTPS_METHOD_POST,
                     httpsexampleHTTPS_POST_PATH,
                     sizeof( httpsexampleHTTPS_POST_PATH ) - 1 );

    /**************************** Disconnect. *****************************/

    prvHTTPSDisconnect();
    configPRINTF( ( "Disconnected from %s:%d\r\n\r\n", httpsexampleHTTPS_SERVER_ADDRESS, httpsexampleHTTPS_SERVER_PORT ) );

    /* Wait between iterations to avoid server throttling */

    configPRINTF( ( "prvHTTPSDemoTask() completed an iteration successfully. "
                    "Total free heap is %u\r\n",
                    xPortGetFreeHeapSize() ) );
    configPRINTF( ( "Demo completed successfully.\r\n" ) );
    configPRINTF( ( "Short delay before starting the next iteration.... \r\n\r\n" ) );
    vTaskDelay( pdMS_TO_TICKS( httpsexampleHTTPS_TIMEOUT_MS ) );
  }
}

Where prvInitialiseLibraries() is defined as:

static void prvInitialiseLibraries( void )
{
IotTaskPoolError_t xTaskPoolResult;
IotHttpsReturnCode_t xHTTPSClientResult;
IotNetworkError_t xNetworkResult;

  /* The HTTPS Client library needs a task pool, so create the system task pool. */
  xTaskPoolResult = IotTaskPool_CreateSystemTaskPool( &( xTaskPoolParameters ) );
  configASSERT( xTaskPoolResult == IOT_TASKPOOL_SUCCESS );

  /* Initialize the network stack abstraction for FreeRTOS. */
  xNetworkResult = IotNetworkFreeRTOS_Init();
  configASSERT( xNetworkResult == IOT_NETWORK_SUCCESS );

  /* HTTPS Client library must be initialized before it can be used. This is
   * just one time initialization. */
  xHTTPSClientResult = IotHttpsClient_Init();
  configASSERT( xHTTPSClientResult == IOT_HTTPS_OK );
}

The screenshot below shows the expected output when the demo is executing correctly. 


Click to enlarge

 

Connecting to the HTTP Server

The function prvHTTPConnect() demonstrates how to establish an unencrypted connection to an HTTP server. It uses the FreeRTOS+TCP network interface which is implemented in the file FreeRTOS-Plus\Source\FreeRTOS-IoT-Libraries\abstractions\platform\freertos\iot_network_freertos.c.

The definition of prvHTTPConnect() is shown below:

static void prvHTTPSConnect( void )
{
  IotHttpsReturnCode_t xHTTPSClientResult;

  /* Establish the connection to the HTTPS server - It is a blocking call and
   * will return only when the connection is complete or a timeout occurs. */
  xHTTPSClientResult = IotHttpsClient_Connect( &( xHTTPSConnection ),
                                               &( xConnectionInfo ) );
  configASSERT( xHTTPSClientResult == IOT_HTTPS_OK );
}

Where xConnectionInfo is defined as:

static const IotHttpsConnectionInfo_t xConnectionInfo =
{
  /* No connection to the HTTPS server has been established yet and we want to
   * establish a new connection. */
  .pAddress = httpsexampleHTTPS_SERVER_ADDRESS,
  .addressLen = sizeof( httpsexampleHTTPS_SERVER_ADDRESS ) - 1,
  .port = httpsexampleHTTPS_SERVER_PORT,
  .userBuffer.pBuffer = ucHTTPSConnectionUserBuffer,
  .userBuffer.bufferLen = sizeof( ucHTTPSConnectionUserBuffer ),

  /* Use FreeRTOS+TCP network. */
  .pNetworkInterface = IOT_NETWORK_INTERFACE_FREERTOS,

  /* The HTTPS Client library uses TLS by default as indicated by the "S"
   * postfixed to "HTTP" in the name of the library and its types and
   * functions. There are no configurations in the flags to enable TLS. */
  .flags = 0,

  /* Optional TLS extensions. For this demo, they are disabled. */
  .pAlpnProtocols = NULL,
  .alpnProtocolsLen = 0,

  /* Provide the certificate for authenticating the server. */
  .pCaCert = NULL,
  .caCertLen = 0,

  /* The HTTPS server at httpbin.org:443 does not require client certificates,
   * but AWS IoT does.
   * If the server were to require a client certificate, the following members
   * need to be set. */
  .pClientCert = NULL,
  .clientCertLen = 0,
  .pPrivateKey = NULL,
  .privateKeyLen = 0
};

 

Sending HTTP Requests

The function prvHTTPRequest() demonstrates how to send a few HTTP requests to the HTTP server. The example demonstrates each of the supported HTTP methods (GET, HEAD, PUT, and POST). A request for each of the methods are listed below. The example code uses the synchronous version of the request send API which will return only when the HTTP response is fully received or a timeout occurs. The definition of the function is shown below:

static void prvHTTPRequest( IotHttpsMethod_t xMethod, 
                                   const char *pcPath, 
                                   uint32_t ulPathLength )
{
IotHttpsReturnCode_t xHTTPClientResult;
IotHttpsRequestInfo_t xHTTPRequestInfo = IOT_HTTPS_REQUEST_INFO_INITIALIZER;
IotHttpsResponseInfo_t xHTTPResponseInfo = IOT_HTTPS_RESPONSE_INFO_INITIALIZER;
IotHttpsRequestHandle_t xHTTPRequest = IOT_HTTPS_REQUEST_HANDLE_INITIALIZER;
IotHttpsResponseHandle_t xHTTPResponse = IOT_HTTPS_RESPONSE_HANDLE_INITIALIZER;
IotHttpsSyncInfo_t xHTTPSyncRequestInfo = IOT_HTTPS_SYNC_INFO_INITIALIZER;
IotHttpsSyncInfo_t xHTTPSyncResponseInfo = IOT_HTTPS_SYNC_INFO_INITIALIZER;
uint16_t usResponseStatus = 0;

  /************************** HTTP request setup. ***************************/

  if( ( xMethod == IOT_HTTPS_METHOD_PUT ) || ( xMethod == IOT_HTTPS_METHOD_POST ) )
  {
    xHTTPSyncRequestInfo.pBody = ( uint8_t* )cHTTPRequestBodyBuffer;
    xHTTPSyncRequestInfo.bodyLen = sizeof( cHTTPRequestBodyBuffer );
  }
  else
  {
    xHTTPSyncRequestInfo.pBody = NULL;
    xHTTPSyncRequestInfo.bodyLen = 0;
  }

  xHTTPRequestInfo.pHost = httpexampleHTTP_SERVER_ADDRESS;
  xHTTPRequestInfo.hostLen = sizeof( httpexampleHTTP_SERVER_ADDRESS ) - 1;
  xHTTPRequestInfo.pPath = pcPath;
  xHTTPRequestInfo.pathLen = ulPathLength;
  xHTTPRequestInfo.method = xMethod;
  xHTTPRequestInfo.isNonPersistent = false;
  xHTTPRequestInfo.userBuffer.pBuffer = ucHTTPRequestUserBuffer;
  xHTTPRequestInfo.userBuffer.bufferLen = sizeof( ucHTTPRequestUserBuffer ) - 1;
  xHTTPRequestInfo.isAsync = false;
  xHTTPRequestInfo.u.pSyncInfo = &xHTTPSyncRequestInfo;

  xHTTPClientResult = IotHttpsClient_InitializeRequest( &xHTTPRequest, &xHTTPRequestInfo );

  /************************** HTTP response setup. **************************/

  memset( ucHTTPResponseBodyBuffer, 0, sizeof( ucHTTPResponseBodyBuffer ) );

  if( xMethod == IOT_HTTPS_METHOD_HEAD )
  {
    xHTTPSyncResponseInfo.pBody = NULL;
    xHTTPSyncResponseInfo.bodyLen = 0;
  }
  else
  {
    xHTTPSyncResponseInfo.pBody = ucHTTPResponseBodyBuffer;
    xHTTPSyncResponseInfo.bodyLen = sizeof( ucHTTPResponseBodyBuffer );
  }

  xHTTPResponseInfo.userBuffer.pBuffer = ucHTTPResponseUserBuffer;
  xHTTPResponseInfo.userBuffer.bufferLen = sizeof( ucHTTPResponseUserBuffer );
  xHTTPResponseInfo.pSyncInfo = &xHTTPSyncResponseInfo;
	
  /*************************** Send HTTP request. ***************************/
	
  /* This synchronous send function blocks until the full response is received
   * from the network. */
  xResult = IotHttpsClient_SendSync( xHTTPConnection, 
                                     xHTTPRequest, 
                                     &xHTTPResponse, 
                                     &xHTTPResponseInfo, 
                                     httpexampleHTTP_TIMEOUT_MS );
  configASSERT( xHTTPClientResult == IOT_HTTPS_OK );
	
  /* The response status is only available if the httpexampleRESPONSE_USER_BUFFER
   * is large enough to fit not only the HTTPS Client response context, but 
   * also the Status-Line of the response. The Status-Line and the response 
   * headers are stored in the provided ucHTTPResponseUserBuffer right after
   * the HTTPS Client response context. */
  xHTTPClientResult = IotHttpsClient_ReadResponseStatus( xHTTPResponse, 
                                                         &usResponseStatus );
  configASSERT( xHTTPClientResult == IOT_HTTPS_OK );
	
  configPRINTF( ( "Response status: %d\r\n", usResponseStatus ) );
  configPRINTF( ( "Response body: \r\n%s\r\n", ucHTTPResponseBodyBuffer ) );
	
  /* The response body may be too large for the print buffer. These extra 
   * carriage returns and newlines help with clobbering. */
  configPRINTF( ( "\r\n\r\n" ) );
}

 

Disconnecting from the HTTP Server

The function prvHTTPDisconnect() demonstrates how to disconnect from the HTTP Server. The definition of the function is shown below:

static void prvHTTPSDisconnect( void )
{
  IotHttpsReturnCode_t xHTTPSClientResult;

  /* The application must always explicitly disconnect from the server with
   * this API if the last request sent on the connection was a persistent
   * request. */
  xHTTPSClientResult = IotHttpsClient_Disconnect( xHTTPSConnection );
  configASSERT( xHTTPSClientResult == IOT_HTTPS_OK );
}
Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.