Network Services User's Guide

Table of Contents

1. Overview

The Network Services (NS) Component provides a set of cross-platform libraries that provide common services related to networking.

At its core is the SlNetSock library that insulates users from the differences between network stacks. For example, users can call SlNetSock_socket() to create a network socket, and that will work whether your network stack is the MSP432E4’s NDK, the CC32XX’s WiFi driver, a Linux-based network stack, or some other.

SlNetSock also provides portable TLS APIs, enabling users to create TLS-aware applications that aren’t bound to a particular network stack or security library. Users who want to bring their own TLS-of-choice-on-top, can ignore the TLS-related SlNetSock APIs and configure their own TLS stack to sit above the [non-secure] SlNetSock APIs.

NS also provides a growing number of application layer networking protocols - such as HTTP/MQTT/SNTP clients, and others. These libraries are inherently portable, as they’re built upon the SlNetSock APIs.

Note that internally most of the NS Component libraries utilize TI-POSIX features. As a result, the NS libraries require an RTOS, as well as a TI-POSIX implementation to be configured into the user’s system. See the examples in your SDK for references.

2. SlNetSock

The SlNetSock Module was created to provide a portable, familiar network socket API that supports multiple network stacks, as well as encompasses network TLS security.

2.1 API Reference Guide

The detailed description of the SlNetSock APIs can be found in the API Reference Guides:

2.2 Usage

To use the SlNetSock APIs, the application should include its header file as follows:

#include <ti/net/slnetsock.h>
#include <ti/net/slnetif.h>
#include <ti/net/slnetutils.h>

At runtime, users must initialize any modules they use:

    SlNetSock_init(0);
    SlNetIf_init(0);
    SlNetUtil_init(0);

Users must also register at least one SlNetIf “interface” that will provide SlNetSock services:

/* To add the NDK network stack */
#include <ti/ndk/slnetif/slnetifndk.h>

    status = SlNetIf_add(SLNETIF_ID_2, ifName,
            (const SlNetIf_Config_t *)&SlNetIfConfigNDK, ndkPri);

For more details, including example code, see the SlNetSock API Reference Guide.

3. HTTPServer

The HTTPServer module enables developers to embed a small HTTP server in an application.

Features

Overview

The HTTPServer module provides a small set of functions to manage one or more instances of an HTTP server. The actual functionality corresponding to each client-specified URL is provided through URL Handler modules that implement the URLHandler interface, described below. It is expected that users will write custom URL Handlers for their systems.

The basic flow of managing an HTTP server is shown in the pseudo code below. (For clarity, return values are ignored - in a real application, these should be checked for errors.)

#include <arpa/inet.h>
#include <netinet/in.h>

#include <ti/net/http/httpserver.h>
#include <ti/net/http/urlhandler.h>

#define NUM_URL_HANDLERS (1)

URLHandler_Setup handlerTable[] = {
    {
        NULL,
        URLFile_create,
        URLFile_delete,
        URLFile_process,
        NULL,
        NULL
    },
};

void * serverTaskFxn(void *arg0)
{
    HTTPServer_Handle srv;
    struct sockaddr_in addr;

    srv = HTTPServer_create(handlerTable, NUM_URL_HANDLERS, NULL);

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(80);

    HTTPServer_serveSelect(srv, (struct sockaddr *)&addr, sizeof(addr), BACKLOG);

    HTTPServer_delete(&srv);
}

int main()
{
    HTTPServer_init();

    Task_create(serverTaskFxn, ...);

    BIOS_start();
}

The HTTPServer module functions are blocking. Specifically HTTPServer_serveSelect() in the example above. This means the function will not return until it completes. So, for typical usage, a task must be created for each HTTP server instance, as shown in main(). The HTTPServer_init() function must be called before using any other HTTPServer functions.

The HTTPServer_serveSelect function will not return until either an error occurs (network outage) or a stop request is made from another thread using HTTPServer_stop(). The return value from the server functions indicates the reason for exiting. After a server instance has returned, it can be deleted using HTTPServer_delete().

A server instance is created using HTTPServer_create() and specifying a table (array) of URL Handlers. You can create a server with no handlers, but then every HTTP client request will result in a 404 - not found error. This can be useful as an initial simple test case.

The HTTPServer_Params structure provides alternate parameter values for the created server’s operation:

After a server instance is created, the actual serving operation is done via:

Security can be enabled via the HTTPServer_enableSecurity() function. Security certificates must be set and loaded via SlNetIf_loadSecObj() and SlNetSock_secAttribSet().

See the HTTPServer API Reference Guide for full details.


3.1 URL Handlers

URL Handlers provide the actual processing of client requests. By separating this processing from the base server in the HTTPServer module, applications can decide exactly how much or how little functionality is required - no response policy is provided in the base server. In a typical application, several URL Handlers could be used:

For each client request received, the server internally loops over all URL Handlers until a handler indicates it has “handled” the request. If no match is found, a “404 - not found” error is returned to the client.

for (i = 0; i < numURLHandlers; i++) {
    if (urlhandler[i].process()) {
        break;
    }
}

This means that the ordering of URL Handlers can be important if there are multiple URL Handlers capable of handling certain URLs.

3.1.1 URL Handler Interface

Each URL Handler has the option to implement any of a set of function pointers defined in the urlhandler.h header file.

typedef URLHandler_Handle (*URLHandler_CreateFxn)(void *params,
                                                  URLHandler_Session session);

typedef void (*URLHandler_DeleteFxn)(URLHandler_Handle *u);
typedef int (*URLHandler_ProcessFxn)(URLHandler_Handle u, int method,
                                     const char *url, const char *urlArgs,
                                     int contentLength, int s);
typedef void (*URLHandler_ScanFieldFxn)(URLHandler_Handle u, int method,
                                        const char *url, const char *field);

These function pointers, along with the HTTPServer_Params structure, make up the URLHandler_Setup structure that is passed into the HTTPServer_create() function.

Every time a client connects to an HTTPServer server instance, a new instance of each URL Handler is created by calling each Handler’s corresponding URLHandler_create() function. This instance will persist for the duration of the client session; i.e., as long as the socket connection persists. When the client disconnects (or is disconnected by the server), the server will delete the handler instance by calling the corresponding delete() function.

For many simple URL Handlers, there will be no ‘state’ needed for a client session, so a create function does not need to be provided. If the URL Handler requires state to be stored, a structure of the user’s choosing needs to be provided. The create() function should allocate memory for the structure and take care of any initialization, before casting the structure to a URLHandler_Handle and returning the pointer to the allocated memory. This handle will be passed around to the other user-defined functions: process(), scan(), and send(). It should be casted back to the user-defined type for storing state before being accessed. The delete() function should clean up the storage structure, i.e., freeing any memory allocated for the structure.

URLHandler_Handle MyURLHandler_create(void *params, URLHandler_Session session)
{
    stateStruct *state = malloc(sizeof(stateStruct));

    if (state)
    {
        state->field1 = ...;
        state->field2 = ...;
        .
        .
        .
    }
    return (URLHandler_Handle)state;
}

Some URL Handlers may provide some parameterization at run-time. This is supported through the first argument of the create() function. This void pointer is supplied via the first field of the URLHandler_Setup structure passed to HTTPServer_create(). A URL Handler can define a structure to hold whatever parameters needed. Users then supply their desired values and pass the pointer to the structure via the URLHandler_Setup structure.

The process() function of a URL Handler can differentiate between requests by examining the URL as well as the HTTP method used (GET, POST, PUT, etc.). The behavior thereafter is up to the user. The server’s response can make use of the functions provided in httpserver_send.c.

int MyURLHandler_process(URLHandler_Handle h, int method, const char *url,
                         const char *urlArgs, int contentLength, int ssock) {
    int processingStatus = URLHandler_ENOTHANDLED;
    int httpRetCode;    // Our return code
    char *responseBody; // Our response

    if (strcmp(url, "/index.html") == 0) {
        if (method == URLHandler_GET) {
            // How was this request handled
            processingStatus = URLHandler_EHANDLED;

            // Our response
            responseBody     = "Index Page";

            // HTTP response status code
            httpRetCode      = HTTP_SC_OK;

            // Send the response ()
            HTTPServer_SendResponse(ssock, httpRetCode, NULL, 0,
                                    strlen(responseBody) + 1, responseBody);
        }
        else {
            processingStatus = URLHandler_EERRORHANDLED;
        }

        return processingStatus;
    }

3.2 Secure Communication

The HTTPServer is capable of communicating over TLS. In order to do so, you must first add the appropriate security parameters to your HTTPServer application. Security parameters are passed to the application as character arrays. i.e.

unsigned char myServerCert[] =
"-----BEGIN CERTIFICATE-----\r\n"
...
"-----END CERTIFICATE-----";

// The size is needed when loading the buffer to a network interface
unsigned int myServerCertLen = sizeof(myServerCert);

unsigned char myPrivateKey[] =
"-----BEGIN CERTIFICATE-----\r\n"
...
"-----END CERTIFICATE-----";

unsigned int myPrivateKeyLen = sizeof(myPrivateKey);

The buffer parameters are then loaded and passed to the application. The functions used below are detailed in the NS API Reference Guide under the SlNetSock and SlNetIf group modules.

int status = 0;
char *certBufName = "myServerCert";
char *pKeyBufName = "myPrivateKey";
SlNetSockSecAttrib_t *secAttribs = NULL;
uint16_t ifID = SLNETIF_ID_2; // Set this to the appropriate interface ID

// Attaching a certificate to the network interface
SlNetIf_loadSecObj(SLNETIF_SEC_OBJ_TYPE_CERTIFICATE, certBufName,
                   strlen(certBufName), myServerCert, myServerCertLen,
                   ifID);

// Attaching a private key to the network interface
SlNetIf_loadSecObj(SLNETIF_SEC_OBJ_TYPE_RSA_PRIVATE_KEY, pKeyBufName,
                   strlen(pKeyBufName), myPrivateKey, myPrivateKeyLen,
                   ifID);

// Creating a security attributes object
// The object returned will hold the items required for a security session
secAttribs = SlNetSock_secAttribCreate();

// Loading the buffers into the security attributes object
status |= SlNetSock_secAttribSet(secAttribs, SLNETSOCK_SEC_ATTRIB_LOCAL_CERT,
                                 certBufName, sizeof(certBufName));

status |= SlNetSock_secAttribSet(secAttribs, SLNETSOCK_SEC_ATTRIB_PRIVATE_KEY,
                                 pKeyBufName, sizeof(pKeyBufName));

After loading security attributes, you are now ready to enable the server’s security features and start the server.

HTTPServer_enableSecurity(srvHandle, secAttribs, true);

HTTPServer_serveSelect(srvHandle, ...)

Now, any client connecting to the server will need to enact a TLS handshake to begin communication.


Server Architecture


Check your SDK for an example of an HTTP server. The SimpleLink SDK provides an example of a secure server.