Download client

The download client library can be used to download files from an HTTP or HTTPS server. It supports IPv4 and IPv6 protocols.

The file is downloaded in fragments of configurable size (CONFIG_DOWNLOAD_CLIENT_MAX_FRAGMENT_SIZE), that are returned to the application via events (DOWNLOAD_CLIENT_EVT_FRAGMENT).

The library can detect the size of the file that is downloaded and sends an event (DOWNLOAD_CLIENT_EVT_DONE) to the application when the download has completed.

The library can detect when the server has closed the connection. When it happens, it returns an event to the application with an appropriate error code. The application can then resume the download by calling the download_client_connect() and download_client_start() functions again.

The download happens in a separate thread which can be paused and resumed.

Make sure to configure CONFIG_DOWNLOAD_CLIENT_MAX_FRAGMENT_SIZE in a way that suits your application. A large fragment size requires more RAM, while a small fragment size results in more download requests, and thus a higher protocol overhead. If the size of the file being downloaded is larger than a hundred times the size of one fragment, the server might close the HTTP connection after the hundredth fragment has been transferred. The library is able to detect when the server has closed the HTTP connection and reconnect automatically. Increasing the fragment size prevents having to establish several HTTP connections and thus helps in keeping protocol overhead to a minimum.

Protocols

The library supports HTTP and HTTPS (TLS 1.2) over IPv4 and IPv6.

HTTP

For HTTP, the following requirements must be met:

  • The application protocol to communicate with the server is HTTP 1.1.
  • IETF RFC 7233 is supported by the HTTP Server.
  • CONFIG_DOWNLOAD_CLIENT_MAX_RESPONSE_SIZE is configured so that it can contain the entire HTTP response.

HTTPS

The library uses TLS version 1.2. When using HTTPS, the application must provision the TLS credentials and pass the security tag to the library when calling download_client_connect().

To provision a TLS certificate to the modem, use nrf_inbuilt_key_write() and other nrf_inbuilt_key APIs from the bdslib/include/nrf_inbuilt_key.h file in the nrfxlib repository. The following snippet illustrates how to provision a TLS certificate, associate it to a TLS security tag, and pass that tag to the library.

#include <nrf_key_mgmt.h>
#include <nrf_inbuilt_key.h>
#include <net/download_client.h>

/* A TLS certificate, in PEM format.
 *
 * CN=DigiCert Baltimore CA-2 G2, OU=www.digicert.com, O=DigiCert Inc, C=US
 *
 * OpenSSL can be used to convert a standard format certificate (X.509)
 * into a PEM format certificate.
 *
 * Note: each line must be terminated by a newline character '\n'.
 * You may need to do this modification manually.
 */
static char certificate[] = {
        "-----BEGIN CERTIFICATE-----\n"
        "MIIEYzCCA0ugAwIBAgIQAYL4CY6i5ia5GjsnhB+5rzANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQG\n"
        "EwJJRTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlC\n"
        "YWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTE1MTIwODEyMDUwN1oXDTI1MDUxMDEyMDAwMFow\n"
        "ZDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2lj\n"
        "ZXJ0LmNvbTEjMCEGA1UEAxMaRGlnaUNlcnQgQmFsdGltb3JlIENBLTIgRzIwggEiMA0GCSqGSIb3\n"
        "DQEBAQUAA4IBDwAwggEKAoIBAQC75wD+AAFz75uI8FwIdfBccHMf/7V6H40II/3HwRM/sSEGvU3M\n"
        "2y24hxkx3tprDcFd0lHVsF5y1PBm1ITykRhBtQkmsgOWBGmVU/oHTz6+hjpDK7JZtavRuvRZQHJa\n"
        "Z7bN5lX8CSukmLK/zKkf1L+Hj4Il/UWAqeydjPl0kM8c+GVQr834RavIL42ONh3e6onNslLZ5QnN\n"
        "NnEr2sbQm8b2pFtbObYfAB8ZpPvTvgzm+4/dDoDmpOdaxMAvcu6R84Nnyc3KzkqwIIH95HKvCRjn\n"
        "T0LsTSdCTQeg3dUNdfc2YMwmVJihiDfwg/etKVkgz7sl4dWe5vOuwQHrtQaJ4gqPAgMBAAGjggEZ\n"
        "MIIBFTAdBgNVHQ4EFgQUwBKyKHRoRmfpcCV0GgBFWwZ9XEQwHwYDVR0jBBgwFoAU5Z1ZMIJHWMys\n"
        "+ghUNoZ7OrUETfAwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwNAYIKwYBBQUH\n"
        "AQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wOgYDVR0fBDMwMTAv\n"
        "oC2gK4YpaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL09tbmlyb290MjAyNS5jcmwwPQYDVR0gBDYw\n"
        "NDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDQYJ\n"
        "KoZIhvcNAQELBQADggEBAC/iN2bDGs+RVe4pFPpQEL6ZjeIo8XQWB2k7RDA99blJ9Wg2/rcwjang\n"
        "B0lCY0ZStWnGm0nyGg9Xxva3vqt1jQ2iqzPkYoVDVKtjlAyjU6DqHeSmpqyVDmV47DOMvpQ+2HCr\n"
        "6sfheM4zlbv7LFjgikCmbUHY2Nmz+S8CxRtwa+I6hXsdGLDRS5rBbxcQKegOw+FUllSlkZUIII1p\n"
        "LJ4vP1C0LuVXH6+kc9KhJLsNkP5FEx2noSnYZgvD0WyzT7QrhExHkOyL4kGJE7YHRndC/bseF/r/\n"
        "JUuOUFfrjsxOFT+xJd1BDKCcYm1vupcHi9nzBhDFKdT3uhaQqNBU4UtJx5g=\n"
        "-----END CERTIFICATE-----"
};

/* The host to connect to */
#define HOST "s3.amazonaws.com"

/* Download client instance */
static struct download_client dl;

int cert_provision_and_connect(void)
{
        int err;

        /* TLS security tag, arbitrary */
        nrf_sec_tag_t sec_tag = 42;

        /* Provision CA Certificate to the modem.
         * The certificate is stored in persistent memory, so
         * it is not necessary to provision it again across reboots.
         */
        err = nrf_inbuilt_key_write(sec_tag, NRF_KEY_MGMT_CRED_TYPE_CA_CHAIN,
                                                                certificate, sizeof(certificate) - 1);
        if (err) {
                return err;
        }

        /* Note:
         * It is assumed, for simplicity, that the download_client library
         * has already been initialized via download_client_init().
         * You need to initialize it in your own application prior to
         * calling download_client_connect().
         */

        /* Specify the security tag in the configuration structure */
        download_client_cfg config = {
                .sec_tag = sec_tag,
        };

        err = download_client_connect(&dl, HOST, &config);
        if (err) {
                return err;
        }

        return 0;
}

Limitations

The library requires the host server to provide a Content-Range field in the HTTP GET header. If this header field is missing, the library logs the following error:

<err> download_client: Server did not send "Content-Range" in response

To debug your application when getting such an error, set the log level of the library to debug (CONFIG_DOWNLOAD_CLIENT_LOG_LEVEL_DBG) and select CONFIG_DOWNLOAD_CLIENT_LOG_HEADERS.

API documentation

Header file: include/download_client.h
Source files: subsys/net/lib/download_client/src/
group dl_client

Client for downloading an object.

The download client provides APIs for:

  • connecting to a remote server,
  • downloading an object from the server,
  • disconnecting from the server,
  • receiving asynchronous event notifications on the download status.

Typedefs

typedef int (*download_client_callback_t)(const struct download_client_evt *event)

Download client asynchronous event handler.

Through this callback, the application receives events, such as download of a fragment, download completion, or errors.

If the callback returns a non-zero value, the download stops. To resume the download, use download_client_start().

Return
Zero to continue the download, non-zero otherwise.
Parameters
  • event: The event.

Enums

enum download_client_evt_id

Download client event IDs.

Values:

DOWNLOAD_CLIENT_EVT_FRAGMENT

Event contains a fragment. The application may return any non-zero value to stop the download.

DOWNLOAD_CLIENT_EVT_ERROR

An error has occurred during download and the connection to the server has been lost.

Error reason may be one of the following:

  • ENOTCONN: socket error during send() or recv()
  • ECONNRESET: peer closed connection
  • EBADMSG: HTTP response header not as expected

In case of network-related errors (ENOTCONN or ECONNRESET), returning zero from the callback will let the library attempt to reconnect to the server and download the last fragment again. Otherwise, the application may return any non-zero value to stop the download.

In case the download is stopped, the application should manually disconnect (download_client_disconnect) to clean up the network socket as necessary before re-attempting the download.

DOWNLOAD_CLIENT_EVT_DONE

Download complete.

Functions

int download_client_init(struct download_client *client, download_client_callback_t callback)

Initialize the download client.

Parameters
  • client: Client instance.
  • callback: Callback function.
Return Value
  • int: Zero on success, otherwise a negative error code.

int download_client_connect(struct download_client *client, const char *host, const struct download_client_cfg *config)

Establish a connection to the server.

Parameters
  • client: Client instance.
  • host: HTTP server to connect to, null-terminated.
  • config: Configuration options.
Return Value
  • int: Zero on success, a negative error code otherwise.

int download_client_start(struct download_client *client, const char *file, size_t from)

Download a file.

The download is carried out in fragments of up to CONFIG_DOWNLOAD_CLIENT_MAX_FRAGMENT_SIZE bytes, which are delivered to the application via DOWNLOAD_CLIENT_EVT_FRAGMENT events.

Parameters
  • client: Client instance.
  • file: File to download, null-terminated.
  • from: Offset from where to resume the download, or zero to download from the beginning.
Return Value
  • int: Zero on success, a negative error code otherwise.

void download_client_pause(struct download_client *client)

Pause the download.

Parameters
  • client: Client instance.

void download_client_resume(struct download_client *client)

Resume the download.

Parameters
  • client: Client instance.

int download_client_file_size_get(struct download_client *client, size_t *size)

Retrieve the size of the file being downloaded, in bytes.

The file size is only available after the download has begun.

Parameters
  • client: Client instance.
  • size: File size.
Return Value
  • int: Zero on success, a negative error code otherwise.

int download_client_disconnect(struct download_client *client)

Disconnect from the server.

Return
Zero on success, a negative error code otherwise.
Parameters
  • client: Client instance.

struct download_client_evt
#include <download_client.h>

Download client event.

Public Members

download_client_evt_id id

Event ID.

int error

Error cause.

struct download_client_cfg
#include <download_client.h>

Download client configuration options.

Public Members

int sec_tag

TLS security tag. If -1, TLS is disabled.

const char *apn

Access point name identifying a packet data network. Must be a null-terminated string or NULL to use the default APN.

struct download_client
#include <download_client.h>

Download client instance.

Public Functions

K_THREAD_STACK_MEMBER(thread_stack, CONFIG_DOWNLOAD_CLIENT_STACK_SIZE)

Internal thread stack.

Public Members

int fd

HTTP socket.

char buf[CONFIG_DOWNLOAD_CLIENT_MAX_RESPONSE_SIZE]

HTTP response buffer.

size_t offset

Buffer offset.

size_t file_size

Size of the file being downloaded, in bytes.

size_t progress

Download progress, number of bytes downloaded.

bool has_header

Whether the HTTP header for the current fragment has been processed.

bool connection_close

The server has closed the connection.

const char *host

Server hosting the file, null-terminated.

const char *file

File name, null-terminated.

struct download_client_cfg config

Configuration options.

k_tid_t tid

Internal thread ID.

struct k_thread thread

Internal download thread.

download_client_callback_t callback

Event handler.