8

Recently I have been working on an IoT project using a MSP430F5529 microcontroller and CC3100 network processor both from Texas Instrument. For evaluation I am using the MSP430F5529 launch pad and CC3100 boosterpack. I am trying to get the device to connect to the cloud. I have successfully implement the CC3100 get weather example application which connects to www.openweathermap.org. This is example is from CC3100 SDK Sample Applications. The program successfully receives and response back from the www.openweathermap.org website. The application uses the GET method make a request from the website.

I have also have successfully tested the code against www.mocky.io. The device receives a status code 200 OK response. But when I test against requestb.in test site I don’t get either a 408 Timeout error response code or a URL redirection response code of 302.

#define WEATHER_SERVER  "api.openweathermap.org"
#define TEST_SERVER  "requestb.in"
//#define TEST_SERVER  "www.mocky.io"

#define PREFIX_BUFFER   "GET /data/2.5/weather?q="
#define POST_BUFFER     "&APPID=xxxxxxxxxxxxxxxxxx&mode=xml&units=imperial HTTP/1.1\r\nHost:api.openweathermap.org\r\nAccept: */"
#define POST_BUFFER2    "*\r\n\r\n"

#define PREFIX_BUFFER_TEST    "GET /1m75pgt1"
#define POST_BUFFER_TEST_1    " HTTP/1.1\r\nHost:requestb.in\r\nAccept: */"
#define POST_BUFFER_TEST_2    "\r\n\r\n"*

//#define PREFIX_BUFFER_TEST      "GET /v2/5967a65d1100007d16b6c2b4"
//#define POST_BUFFER_TEST_1    " HTTP/1.1\r\nHost:www.mocky.io\r\nAccept: */"
//#define POST_BUFFER_TEST_2    "\r\n\r\n"*

Below is the main which includes some of setup conditions. Some of the error handling code has been removed for brevity.

 int main(int argc, char** argv)
{
    _i32 retVal = -1;

    retVal = initializeAppVariables();
    ASSERT_ON_ERROR(retVal);



    /* Stop WDT and initialize the system-clock of the MCU */
    stopWDT();
    initClk();


    /*
     * Following function configures the device to default state by cleaning
     * the persistent settings stored in NVMEM (viz. connection profiles &
     * policies, power policy etc)
     *
     * Applications may choose to skip this step if the developer is sure
     * that the device is in its default state at start of application
     *
     * Note that all profiles and persistent settings that were done on the
     * device will be lost
     */
    retVal = configureSimpleLinkToDefaultState();


    /*
     * Assumption is that the device is configured in station mode already
     * and it is in its default state
     */
    retVal = sl_Start(0, 0, 0);

    /* Connecting to WLAN AP */
    retVal = establishConnectionWithAP();

    retVal = getCredentials();

    retVal = disconnectFromAP();

    return 0;
}

Below is getCredentials() code which calls get data.

<!-- language: lang-c -->
static _i32 getCredentials()
{
    _i32 retVal = -1;

    pal_Strcpy((char *)g_DeviceData.HostName, TEST_SERVER);

    retVal = getHostIP_Device();

    g_DeviceData.SockID = createConnection();
    ASSERT_ON_ERROR(g_DeviceData.SockID);

    retVal = getData();
    ASSERT_ON_ERROR(retVal);

    retVal = sl_Close(g_DeviceData.SockID);
    ASSERT_ON_ERROR(retVal);

    return 0;
}

Below is a getdata() function where I am getting the error.

/*!
    \brief This function Obtains the required data from the server

    \param[in]      none

    \return         0 on success, -ve otherwise

    \note

    \warning
*/
static _i32 getData()
{
    _u8 *p_startPtr = NULL;
    _u8 *p_endPtr = NULL;
    _u8* p_bufLocation = NULL;
    _i32 retVal = -1;

    pal_Memset(g_DeviceData.Recvbuff, 0, sizeof(g_DeviceData.Recvbuff));

    /* Puts together the HTTP GET string. */
    p_bufLocation = g_DeviceData.SendBuff;

    pal_Strcpy(p_bufLocation, PREFIX_BUFFER_TEST);
    p_bufLocation += pal_Strlen(PREFIX_BUFFER_TEST);

    pal_Strcpy(p_bufLocation, POST_BUFFER_TEST_1);
    p_bufLocation += pal_Strlen(POST_BUFFER_TEST_1);

    pal_Strcpy(p_bufLocation, POST_BUFFER_TEST_2);

    /* Send the HTTP GET string to the open TCP/IP socket. */
    retVal = sl_Send(g_DeviceData.SockID, g_DeviceData.SendBuff, pal_Strlen(g_DeviceData.SendBuff), 0);
    if(retVal != pal_Strlen(g_DeviceData.SendBuff))
        ASSERT_ON_ERROR(HTTP_SEND_ERROR);

    /* Receive response */
    retVal = sl_Recv(g_DeviceData.SockID, &g_DeviceData.Recvbuff[0], MAX_SEND_RCV_SIZE, 0);
    if(retVal <= 0)
        ASSERT_ON_ERROR(HTTP_RECV_ERROR);

    g_DeviceData.Recvbuff[pal_Strlen(g_DeviceData.Recvbuff)] = '\0';
    return SUCCESS;
}

The security for the access point is setup as

#define SEC_TYPE        SL_SEC_TYPE_WPA_WPA2    /* Security type of the Access point */

Finally there are few POC sensor devices fabricated with CC3100 that need to transfer data to the cloud. For simplicity we are using the boosterpack, eventually we need to get the POC sensor devices communicating to cloud via Wifi.

6
  • I'm voting to close this question as off-topic because the actual issue of the question is about basic HTTP usage and to a lesser degree a particular embedded platform, not the Internet of Things, therefore the pool of commonality is to other HTTP questions first, and not really to other IoT questions at all. – Chris Stratton Jul 22 '17 at 14:48
  • 1
    FYI the specific requestb.in instance in your code has apparently timed out and now 404's when accessed via HTTP over TLS with SNI as works for currently valid ones. – Chris Stratton Jul 23 '17 at 17:10
  • @mico, I noticed that you have removed your answer. Also you mentioned adding a "s" to http. I am not sure how I can achieve your suggestion. Can you please give me some insights. – user8055 Jul 25 '17 at 15:16
  • @ChrisStratton, Sorry hear that you voting to close this questions. Can you please recommend me a good Q&A site to ask this question? – user8055 Jul 25 '17 at 15:18
  • Do you get those 302 and 408 messages or not? If not, then what messages you do get? – mico Jul 25 '17 at 15:46
5

The difference is because the requestb.in site requires HTTP over TLS with SNI

This is a fairly complex HTTP/HTTPS protocols and security question likely better addressed elsewhere in the SE system, and mostly concerning details of the requestb.in service you are testing against rather than whatever IoT project you might eventually be doing. But so long as it remains here...


Debugging the requestb.in response code when attempting HTTP on port 80

You said:

But when I test against requestb.in test site I don’t get either a 408 Timeout error response code or a URL redirection response code of 302.

Let's see what happens:

curl -i http://requestb.in/xxxxxxxx
HTTP/1.1 301 Moved Permanently
Location: https://requestb.in/xxxxxxxx

HTTP status 301 is "Moved Permanently", while the 302 you were expecting is sometimes used for a temporary redirect. Since they're probably not planning to ever let you use HTTP on a plain TCP socket on port 80, the permanent redirect they send is the more correct response.

It would have been a lot more helpful if you had captured the status code which _was_ sent rather than merely listing two which _were not_ sent

At any rate, what the server is telling is that we must use HTTPS.


So what would you need to do to connect to the server via HTTPS?

At a basic level, HTTPS simply means speaking HTTP over a connection secured with TLS (or previously, SSL), rather than doing so over a plain TCP socket.

Your CC3100 supports TLS and SSL, and there's some minimal information about modifying one of the SDK's HTTP client examples to perform an HTTPS connection by instead making a secure channel, available at http://processors.wiki.ti.com/index.php/CC3100_HTTP_Client

However, in the case of requestb.in this may well not be sufficient.

If you were to pipe a very simple GET request into openssl's s_client command (or do the same thing on the CC3100)

(echo -en "GET /xxxxxxxx HTTP/1.1\nHost: requestb.in\n\n" ; sleep 10) | \
openssl s_client -connect requestb.in:443

(the sleep is to make openssl wait for a response rather than hangup at the conclusion of the input)

You'd get an odd error:

SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure

You might thing this has something to do with SSL vs. TLS versions, but actually it's because requestb.in requires something called Server Name Indication (Wikipedia). This effectively means you need to tell the SSL stack to tell the server which servername it should authenticate as.

Continuing with the command line demonstrations, we'd simply add a -servername argument:

(echo -en "GET /xxxxxxxx HTTP/1.1\nHost: requestb.in\n\n" ; sleep 10) |\
openssl s_client -connect requestb.in:443 -servername requestb.in

If this is done with the URL hash of a valid requestb.in instance, it produces a result and is visible in the log panel of the browser which created that.


Implementing Server Name Indication (SNI) on the CC3xxx

There are some forum posts suggesting that the TLS implementation on the CC3xxx has not supported SNI, and that there aren't concrete plans to add this; however, you could verify if that is still the case.

But it's important to remember that there's a tree of network layers involved:

WiFi
  IP packets
    TCP session
      TLS session
        HTTP protocol

Since the CC3100 allows you to speak TCP (and in fact you were doing so in your existing code), you're free to "bring your own" TLS implementation that does have SNI support. For example, this blog post discusses porting the mbed TLS (formerly PolarSSL) libary to the CC3xxx and mentions SNI as capability.

TL;DR

requestb.in is a challenging target as it requires that you speak HTTP over a session secured with TLS and implementing Server Name Indication. If talking to this host isn't part of your ultimate project you might find it better to simply move on to those hosts which are - those intended for use with minimal embedded clients on IoT devices might well be configured to make things a bit easier by not using SNI.

It's also going to be far more efficient if you get in the habit of capturing the details of problems you encounter - embedded development is always challenging to debug, and the less information about failures you capture via logging or a debugger, the more time spent guessing.

3
  • Looks a good answer, but is beyond my comprehension. I hope achieve this level of in-depth knowledge in the near future. But thank you very much for the response. – user8055 Jul 25 '17 at 15:20
  • 1
    The SNI requirement does indeed make it complicated - my recommendation would be to skip requestb.in, concentrate on the requirements of whatever you're actually trying to talk to in your real project, and if necessary find a mocking service that is more similar to that requirement. – Chris Stratton Jul 25 '17 at 16:09
  • Now when reading this and after deleting two of my competitive efforts, I suggest the same, do something more productive. And do accept Chris' effort. – mico Jul 25 '17 at 17:23
1

It is highly likely that both openweathermap.org and www.mocky.io support http or port 80. Below are few clues.

openweathermap.org

Openweathermap

openweathermap-url

www.mocky.io

mocky

mocky-url

Looks like requestb.in support HTTPS or port 443 only.

requestb.in

requestb

requestb-url

This might explain the issue. Below are few references that might help.

In the CC3100 datasheet there are no references to CC3100 working with HTTPS. All references are to HTTP only. This probably better explains the issue. Attach below an except from the datasheet.

CC3100 - Datasheet excepts

Looks like per datasheet CC3120 supports HTTPS. Attach below is except from the datasheet. There are few references to HTTPS.

CC3100 - Datasheet excepts

The most likely path forward is replace the CC3100 with CC3120.

References:

2
  • 1
    After numerous edits introducing errors and then correcting them, this might now be approaching being an actual answer. But you really still need to rewrite it: if your thesis is that one site supports http and the other only https, then just say that up front. – Chris Stratton Jul 22 '17 at 15:06
  • 2
    And your analysis of the CC3100 and replacement recommendation remains entirely erroneous. It explicitly says it supports TLS/SSL, while you try to argue that it doesn't by erroneously looking at it's web server capability, when the question has nothing to do with being a server. The question is actually not using an HTTP library at all, but doing it manually atop a TCP socket. To contact an HTTPS server, it would need to perform similar operations atop an SSL connection. – Chris Stratton Jul 22 '17 at 15:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.