diff --git a/.github/test-spec.yml b/.github/test-spec.yml index 8b4e294a7850..38a312ce34a0 100644 --- a/.github/test-spec.yml +++ b/.github/test-spec.yml @@ -38,7 +38,7 @@ - "lib/nrf_modem_lib/**/*" - "lib/sms/**/*" - "subsys/dfu/dfu_target/**/*" - - "subsys/net/lib/download_client/**/*" + - "subsys/net/lib/downloader/**/*" - "subsys/net/lib/fota_download/**/*" - "subsys/net/lib/ftp_client/**/*" - "subsys/net/lib/nrf_cloud/**/*" @@ -51,7 +51,7 @@ - "lib/modem_info/**/*" - "lib/modem_key_mgmt/**/*" - "lib/date_time/**/*" - - "subsys/net/lib/download_client/**/*" + - "subsys/net/lib/downloader/**/*" - "subsys/net/lib/fota_download/**/*" - "subsys/net/lib/lwm2m_client_utils/**/*" - "subsys/dfu/dfu_target/**/*" @@ -81,7 +81,7 @@ - "include/modem/nrf_modem_lib.h" - "include/modem/pdn.h" - "include/modem/sms.h" - - "include/net/download_client.h" + - "include/net/downloader.h" - "include/secure_services.h" - "lib/at_host/**/*" - "lib/at_monitor/**/*" @@ -102,7 +102,7 @@ - "samples/cellular/pdn/**/*" - "samples/cellular/sms/**/*" - "samples/cellular/udp/**/*" - - "subsys/net/lib/download_client/**/*" + - "subsys/net/lib/downloader/**/*" "CI-lwm2m-test": - "include/modem/lte_lc*.h" @@ -114,8 +114,8 @@ - "samples/cellular/lwm2m_carrier/**/*" - "include/modem/at_monitor.h" - "lib/at_monitor/**/*" - - "include/net/download_client.h" - - "subsys/net/lib/download_client/**/*" + - "include/net/downloader.h" + - "subsys/net/lib/downloader/**/*" - "lib/sms/**/*" - "include/modem/sms.h" - "lib/pdn/**/*" @@ -147,7 +147,7 @@ - "subsys/mgmt/**/*" - "subsys/pcd/*" - "subsys/net/lib/*fota*/**/*" - - "subsys/net/lib/download_client/**/*" + - "subsys/net/lib/downloader/**/*" - "tests/subsys/bootloader/**/*" - "tests/subsys/dfu/**/*" @@ -433,7 +433,7 @@ - "include/modem/nrf_modem_lib.h" - "include/modem/pdn.h" - "include/modem/sms.h" - - "include/net/download_client.h" + - "include/net/downloader.h" - "include/net/fota_download.h" - "include/net/nrf_cloud.h" - "include/net/nrf_cloud_agnss.h" @@ -457,7 +457,7 @@ - "lib/sms/**/*" - "lib/supl/**/*" - "samples/cellular/modem_shell/**/*" - - "subsys/net/lib/download_client/**/*" + - "subsys/net/lib/downloader/**/*" - "subsys/net/lib/fota_download/**/*" - "subsys/net/lib/nrf_cloud/**/*" - "subsys/net/lib/rest_client/**/*" @@ -509,7 +509,7 @@ - "include/drivers/flash/**/*" - "include/flash_map_pm.h" - "include/modem/**/*" - - "include/net/download_client.h" + - "include/net/downloader.h" - "include/net/fota_download.h" - "include/net/nrf_cloud*" - "include/net/rest_client.h" @@ -531,7 +531,7 @@ - "subsys/bootloader/**/*" - "subsys/caf/**/*" - "subsys/dfu/**/*" - - "subsys/net/lib/download_client/**/*" + - "subsys/net/lib/downloader/**/*" - "subsys/net/lib/fota_download/**/*" - "subsys/net/lib/nrf_cloud/**/*" - "subsys/net/lib/nrf_provisioning/**/*" diff --git a/CODEOWNERS b/CODEOWNERS index ddfe726966be..1103e64cb53e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -167,6 +167,7 @@ /doc/nrf/libraries/networking/azure_*.rst @nrfconnect/ncs-cia-doc /doc/nrf/libraries/networking/coap_utils.rst @nrfconnect/ncs-terahertz-doc /doc/nrf/libraries/networking/download_client.rst @nrfconnect/ncs-modem-doc +/doc/nrf/libraries/networking/downloader.rst @nrfconnect/ncs-modem-doc /doc/nrf/libraries/networking/fota_download.rst @nrfconnect/ncs-pluto-doc /doc/nrf/libraries/networking/ftp_client.rst @nrfconnect/ncs-iot-oulu-tampere-doc /doc/nrf/libraries/networking/icalendar_parser.rst @nrfconnect/ncs-doc-leads @@ -335,7 +336,10 @@ /include/mgmt/ @nrfconnect/ncs-pluto /include/modem/ @nrfconnect/ncs-modem /include/mpsl/ @nrfconnect/ncs-dragoon +/include/net/ @nrfconnect/ncs-co-networking /include/net/azure_* @nrfconnect/ncs-cia +/include/net/download_client* @nrfconnect/ncs-modem +/include/net/downloader* @nrfconnect/ncs-modem /include/net/nrf_cloud_* @nrfconnect/ncs-nrf-cloud /include/net/wifi_credentials.h @nrfconnect/ncs-cia /include/nfc/ @nrfconnect/ncs-co-drivers @nrfconnect/ncs-si-muffin @@ -743,6 +747,8 @@ /subsys/net/lib/mqtt_helper/ @nrfconnect/ncs-cia /subsys/net/lib/azure_* @nrfconnect/ncs-cia /subsys/net/lib/aws_* @nrfconnect/ncs-cia +/subsys/net/lib/download_client* @nrfconnect/ncs-modem +/subsys/net/lib/downloader/ @nrfconnect/ncs-modem /subsys/net/lib/ftp_client/ @nrfconnect/ncs-iot-oulu /subsys/net/lib/hostap_crypto/ @krish2718 @jukkar @vivekuppunda /subsys/net/lib/icalendar_parser/ @lats1980 @@ -852,6 +858,8 @@ /tests/subsys/mpsl/ @nrfconnect/ncs-dragoon /tests/subsys/net/lib/aws_*/ @nrfconnect/ncs-cia /tests/subsys/net/lib/azure_iot_hub/ @nrfconnect/ncs-cia +/tests/subsys/net/lib/downloader/ @nrfconnect/ncs-modem +/tests/subsys/net/lib/download_client/ @nrfconnect/ncs-modem /tests/subsys/net/lib/fota_download/ @nrfconnect/ncs-pluto /tests/subsys/net/lib/lwm2m_*/ @nrfconnect/ncs-iot-oulu /tests/subsys/net/lib/mqtt_helper/ @nrfconnect/ncs-cia diff --git a/applications/asset_tracker_v2/boards/native_sim.conf b/applications/asset_tracker_v2/boards/native_sim.conf index 468ffc505eb7..e337465a8bc6 100644 --- a/applications/asset_tracker_v2/boards/native_sim.conf +++ b/applications/asset_tracker_v2/boards/native_sim.conf @@ -103,8 +103,7 @@ CONFIG_NET_CONFIG_MY_IPV4_GW="192.0.2.2" # FOTA CONFIG_FOTA_DOWNLOAD=n CONFIG_DFU_TARGET=n -CONFIG_DOWNLOAD_CLIENT=n -CONFIG_DOWNLOAD_CLIENT_HTTP_FRAG_SIZE_1024=n +CONFIG_DOWNLOADER=n # MCUBOOT CONFIG_BOOTLOADER_MCUBOOT=n diff --git a/applications/asset_tracker_v2/doc/asset_tracker_v2_description.rst b/applications/asset_tracker_v2/doc/asset_tracker_v2_description.rst index 601ec267801e..598efa9003df 100644 --- a/applications/asset_tracker_v2/doc/asset_tracker_v2_description.rst +++ b/applications/asset_tracker_v2/doc/asset_tracker_v2_description.rst @@ -235,7 +235,7 @@ This application uses the following |NCS| libraries and drivers: * :ref:`lib_date_time` * :ref:`lte_lc_readme` * :ref:`modem_info_readme` -* :ref:`lib_download_client` +* :ref:`lib_downloader` * :ref:`lib_fota_download` * :ref:`caf_leds` diff --git a/applications/asset_tracker_v2/overlay-carrier.conf b/applications/asset_tracker_v2/overlay-carrier.conf index f28a35093f29..e629f83a1896 100644 --- a/applications/asset_tracker_v2/overlay-carrier.conf +++ b/applications/asset_tracker_v2/overlay-carrier.conf @@ -18,8 +18,8 @@ CONFIG_PDN=y # AT Monitor is used by PDN library CONFIG_AT_MONITOR=y -# Download client for DFU -CONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE=230 +# Downloader for DFU +CONFIG_DOWNLOADER_MAX_FILENAME_SIZE=230 # Modem info CONFIG_MODEM_INFO_BUFFER_SIZE=512 @@ -36,3 +36,6 @@ CONFIG_DFU_TARGET=y # Asserts CONFIG_ASSERT_VERBOSE=n + +# IPv4 (IPv6 is enabled by default) +CONFIG_NET_IPV4=y diff --git a/applications/asset_tracker_v2/prj.conf b/applications/asset_tracker_v2/prj.conf index 7c6e966c1456..e1d8c07e2e7a 100644 --- a/applications/asset_tracker_v2/prj.conf +++ b/applications/asset_tracker_v2/prj.conf @@ -60,11 +60,9 @@ CONFIG_FCB=y # FOTA CONFIG_FOTA_DOWNLOAD=y CONFIG_DFU_TARGET=y -CONFIG_DOWNLOAD_CLIENT=y -CONFIG_DOWNLOAD_CLIENT_HTTP_FRAG_SIZE_1024=y -CONFIG_DOWNLOAD_CLIENT_STACK_SIZE=4096 -CONFIG_DOWNLOAD_CLIENT_BUF_SIZE=2300 -CONFIG_DOWNLOAD_CLIENT_MAX_HOSTNAME_SIZE=128 +CONFIG_DOWNLOADER=y +CONFIG_DOWNLOADER_STACK_SIZE=4096 +CONFIG_DOWNLOADER_MAX_HOSTNAME_SIZE=128 # Flash - Used in FOTA, settings and storage for P-GPS. CONFIG_FLASH=y diff --git a/applications/serial_lte_modem/doc/slm_description.rst b/applications/serial_lte_modem/doc/slm_description.rst index ab2ed3eaac63..1f1ff3c352ec 100644 --- a/applications/serial_lte_modem/doc/slm_description.rst +++ b/applications/serial_lte_modem/doc/slm_description.rst @@ -684,7 +684,7 @@ This application uses the following |NCS| libraries: * :ref:`lib_ftp_client` * :ref:`sms_readme` * :ref:`lib_fota_download` -* :ref:`lib_download_client` +* :ref:`lib_downloader` * :ref:`lib_nrf_cloud` * :ref:`lib_nrf_cloud_agnss` * :ref:`lib_nrf_cloud_pgps` diff --git a/applications/serial_lte_modem/overlay-carrier.conf b/applications/serial_lte_modem/overlay-carrier.conf index c0f1e53d429d..6f18ebe451f2 100644 --- a/applications/serial_lte_modem/overlay-carrier.conf +++ b/applications/serial_lte_modem/overlay-carrier.conf @@ -13,7 +13,7 @@ CONFIG_FP_HARDABI=y CONFIG_PDN=y # Download client for DFU -CONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE=230 +CONFIG_DOWNLOADER_MAX_FILENAME_SIZE=230 # Modem info CONFIG_MODEM_INFO_BUFFER_SIZE=512 @@ -28,3 +28,6 @@ CONFIG_LWM2M_CARRIER_SETTINGS=y # DFU target library CONFIG_DFU_TARGET=y + +# IPv4 (IPv6 is enabled by default) +CONFIG_NET_IPV4=y diff --git a/applications/serial_lte_modem/prj.conf b/applications/serial_lte_modem/prj.conf index 8130326d3728..222c1d3acd9e 100644 --- a/applications/serial_lte_modem/prj.conf +++ b/applications/serial_lte_modem/prj.conf @@ -62,9 +62,9 @@ CONFIG_HTTP_PARSER_URL=y CONFIG_FOTA_DOWNLOAD=y CONFIG_FOTA_DOWNLOAD_PROGRESS_EVT=y CONFIG_DFU_TARGET=y -CONFIG_DOWNLOAD_CLIENT=y -CONFIG_DOWNLOAD_CLIENT_STACK_SIZE=4096 -CONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE=2048 +CONFIG_DOWNLOADER=y +CONFIG_DOWNLOADER_STACK_SIZE=4096 +CONFIG_DOWNLOADER_MAX_FILENAME_SIZE=2048 CONFIG_BOOTLOADER_MCUBOOT=y CONFIG_IMG_MANAGER=y diff --git a/applications/serial_lte_modem/src/slm_at_fota.c b/applications/serial_lte_modem/src/slm_at_fota.c index 4fffd1dad74d..962b9ee5f8b0 100644 --- a/applications/serial_lte_modem/src/slm_at_fota.c +++ b/applications/serial_lte_modem/src/slm_at_fota.c @@ -26,10 +26,10 @@ LOG_MODULE_REGISTER(slm_fota, CONFIG_SLM_LOG_LEVEL); /* file_uri: scheme://hostname[:port]path[?parameters] */ -#define FILE_URI_MAX CONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE +#define FILE_URI_MAX CONFIG_DOWNLOADER_MAX_FILENAME_SIZE #define SCHEMA_HTTP "http" #define SCHEMA_HTTPS "https" -#define URI_HOST_MAX CONFIG_DOWNLOAD_CLIENT_MAX_HOSTNAME_SIZE +#define URI_HOST_MAX CONFIG_DOWNLOADER_MAX_HOSTNAME_SIZE #define URI_SCHEMA_MAX 8 #define ERASE_POLL_TIME 2 diff --git a/doc/nrf/app_dev/device_guides/nrf91/nrf91_building.rst b/doc/nrf/app_dev/device_guides/nrf91/nrf91_building.rst index 5f0fb8601f6f..3d0f6dfbef00 100644 --- a/doc/nrf/app_dev/device_guides/nrf91/nrf91_building.rst +++ b/doc/nrf/app_dev/device_guides/nrf91/nrf91_building.rst @@ -71,7 +71,7 @@ To perform a FOTA update, complete the following steps: See :ref:`upgradable_bootloader` for more information. #. Make the binary file (or files) available for download. - Upload the serialized :file:`.cbor` binary file or files to a web server that is compatible with the :ref:`lib_download_client` library. + Upload the serialized :file:`.cbor` binary file or files to a web server that is compatible with the :ref:`lib_downloader` library. The full FOTA procedure depends on where the binary files are hosted for download. diff --git a/doc/nrf/libraries/bin/lwm2m_carrier/app_integration.rst b/doc/nrf/libraries/bin/lwm2m_carrier/app_integration.rst index c2f93fd1d9ab..ed82e52b6abf 100644 --- a/doc/nrf/libraries/bin/lwm2m_carrier/app_integration.rst +++ b/doc/nrf/libraries/bin/lwm2m_carrier/app_integration.rst @@ -31,7 +31,7 @@ It provides an abstraction of the following modules: .. lwm2m_osal_mod_list_start * :ref:`at_monitor_readme` - * :ref:`lib_download_client` + * :ref:`lib_downloader` * :ref:`sms_readme` * :ref:`pdn_readme` * :ref:`lib_dfu_target` diff --git a/doc/nrf/libraries/bin/lwm2m_carrier/requirements.rst b/doc/nrf/libraries/bin/lwm2m_carrier/requirements.rst index d69277af272d..2b3b37c172d6 100644 --- a/doc/nrf/libraries/bin/lwm2m_carrier/requirements.rst +++ b/doc/nrf/libraries/bin/lwm2m_carrier/requirements.rst @@ -48,7 +48,7 @@ Following are some of the requirements and limitations of the application while * For example, setting :kconfig:option:`CONFIG_LWM2M_CARRIER_SERVER_SEC_TAG` to 42 uses the security tag range 43 to 46 instead of 25 to 28. * The CA certificates that are used for out-of-band FOTA must be provided by the application. - Out-of-band FOTA updates are done by the :ref:`lib_download_client`. + Out-of-band FOTA updates are done by the :ref:`lib_downloader`. Although the certificates are updated as part of the |NCS| releases, you must check the requirements from your carrier to know which certificates are applicable. * The LwM2M carrier library uses the following NVS record key range: ``0xCA00`` to ``0xCAFF``. diff --git a/doc/nrf/libraries/networking/aws_fota.rst b/doc/nrf/libraries/networking/aws_fota.rst index 9ed2a33e3b42..cbeacde6ff26 100644 --- a/doc/nrf/libraries/networking/aws_fota.rst +++ b/doc/nrf/libraries/networking/aws_fota.rst @@ -62,7 +62,7 @@ Creating a FOTA job #. Click the uploaded image file :file:`app_update.bin` and copy the *Object URL* without the *https://* prefix and folder path. #. Create a text file (job document) with content as in the snippet, replacing the following data: - * *protocol* with either `http` or `https`. + * *protocol* with either ``http`` or ``https``. * *host_url* with the *Object URL* copied in the previous step (for example, ``examplebucket.s3.eu-central-1.amazonaws.com``). * *file_path* with the path and file name (for example, ``app_update.bin``). @@ -105,10 +105,10 @@ Configure the following parameters when using this library: * :kconfig:option:`CONFIG_AWS_FOTA_PAYLOAD_SIZE` - Sets the maximum payload size for AWS IoT job messages. * :kconfig:option:`CONFIG_AWS_FOTA_DOWNLOAD_SECURITY_TAG` - Sets the security tag to be used in case of HTTPS downloads. -Additionally, configure the :ref:`lib_download_client` library: +Additionally, configure the :ref:`lib_downloader` library: -* :kconfig:option:`CONFIG_DOWNLOAD_CLIENT_MAX_HOSTNAME_SIZE` - Sets the maximum length of the host name for the download client. -* :kconfig:option:`CONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE` - Sets the maximum length of the file name for the download client. +* :kconfig:option:`CONFIG_DOWNLOADER_MAX_HOSTNAME_SIZE` - Sets the maximum length of the host name for the library. +* :kconfig:option:`CONFIG_DOWNLOADER_MAX_FILENAME_SIZE` - Sets the maximum length of the file name for the library. .. _aws_fota_implementation: @@ -133,7 +133,7 @@ The following sequence diagram shows how a firmware over-the-air update is imple AWS IoT jobs ============ -The implementation uses a job document like the following (where *protocol* is either `http` or `https`, *bucket_name* is the name of your bucket and *file_name* is the name of your file) for passing information from `AWS IoT jobs`_ to the device: +The implementation uses a job document like the following (where *protocol* is either ``http`` or ``https``, *bucket_name* is the name of your bucket and *file_name* is the name of your file) for passing information from `AWS IoT jobs`_ to the device: .. parsed-literal:: :class: highlight @@ -191,7 +191,7 @@ Presigned URLs When using the presigned URLs, you might need to increase the value of the following Kconfig options to accommodate the long file name and payload size of the presigned URL and the secure download of the image: -* :kconfig:option:`CONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE`. +* :kconfig:option:`CONFIG_DOWNLOADER_MAX_FILENAME_SIZE`. * :kconfig:option:`CONFIG_AWS_FOTA_PAYLOAD_SIZE`. * :kconfig:option:`CONFIG_MBEDTLS_HEAP_SIZE` - If running Mbed TLS on the application core (Wi-FiĀ® builds). @@ -199,9 +199,9 @@ Limitations *********** * If the :kconfig:option:`CONFIG_AWS_FOTA_DOWNLOAD_SECURITY_TAG` Kconfig option is not configured but HTTPS is selected as the protocol, the update job fails. - For further information about HTTPS support, refer to :ref:`the HTTPS section of the download client documentation `. + For further information about HTTPS support, refer to :ref:`the HTTPS section of the Downloader library documentation `. * The library requires a Content-Range header to be present in the HTTP response from the server. - This limitation is inherited from the :ref:`lib_download_client` library. + This limitation is inherited from the :ref:`lib_downloader` library. API documentation ***************** diff --git a/doc/nrf/libraries/networking/azure_fota.rst b/doc/nrf/libraries/networking/azure_fota.rst index ba04327482ff..f906aa7de4db 100644 --- a/doc/nrf/libraries/networking/azure_fota.rst +++ b/doc/nrf/libraries/networking/azure_fota.rst @@ -58,16 +58,16 @@ Configure the following parameters when using this library: * :kconfig:option:`CONFIG_AZURE_FOTA_TLS` - Enables HTTPS for downloads. By default, TLS is enabled and currently, the transport protocol must be configured at compile time. * :kconfig:option:`CONFIG_AZURE_FOTA_SEC_TAG` - Sets the security tag for TLS credentials when using HTTPS as the transport layer. See :ref:`azure_iot_hub_flash_certs` for more details. -Additionally, configure the :ref:`lib_download_client` library: +Additionally, configure the :ref:`lib_downloader` library: -* :kconfig:option:`CONFIG_DOWNLOAD_CLIENT_MAX_HOSTNAME_SIZE` - Sets the maximum length of the host name for the download client. -* :kconfig:option:`CONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE` - Sets the maximum length of the file name for the download client. +* :kconfig:option:`CONFIG_DOWNLOADER_MAX_HOSTNAME_SIZE` - Sets the maximum length of the host name for the library. +* :kconfig:option:`CONFIG_DOWNLOADER_MAX_FILENAME_SIZE` - Sets the maximum length of the file name for the library. Limitations *********** The library requires a ``Content-Range`` header to be present in the HTTP response from the server. -This limitation is inherited from the :ref:`lib_download_client` library. +This limitation is inherited from the :ref:`lib_downloader` library. API documentation ***************** diff --git a/doc/nrf/libraries/networking/download_client.rst b/doc/nrf/libraries/networking/download_client.rst index 177184d9fc32..1628ffc16fe7 100644 --- a/doc/nrf/libraries/networking/download_client.rst +++ b/doc/nrf/libraries/networking/download_client.rst @@ -7,6 +7,11 @@ Download client :local: :depth: 2 +.. note:: + + The :ref:`lib_download_client` library has been deprecated and it will be removed in one of the future releases. + Use the :ref:`lib_downloader` library instead. + The download client library can be used to download files from an HTTP or a CoAP server. Overview diff --git a/doc/nrf/libraries/networking/downloader.rst b/doc/nrf/libraries/networking/downloader.rst new file mode 100644 index 000000000000..a938df10c9a1 --- /dev/null +++ b/doc/nrf/libraries/networking/downloader.rst @@ -0,0 +1,203 @@ +.. _lib_downloader: + +Downloader +########## + +.. contents:: + :local: + :depth: 2 + +You can use the downloader library to download files from a server. + +Overview +******** + +The download is carried out in a separate thread and the application receives events such as :c:enumerator:`DOWNLOADER_EVT_FRAGMENT` that contain the data fragments as the download progresses. +When the download completes, the library sends the :c:enumerator:`DOWNLOADER_EVT_DONE` event to the application. + +Protocols +========= + +The library supports HTTP, HTTPS (TLS 1.2), CoAP, and CoAPS (DTLS 1.2) over IPv4 and IPv6. +If other protocols are required, they can be added by the application. +See :file:`downloader_transport.h` for details. +The protocol used for the download is specified in the beginning of the ``URI`` or ``host``. +Use ``http://`` for HTTP, ``https://`` for HTTPS, ``coap://`` for CoAP, and ``coaps://`` for CoAPS. +If no protocol is specified, the downloader defaults to HTTP or HTTPS, depending on the server security configuration. + +.. _downloader_https: + +HTTP and HTTPS (TLS 1.2) +------------------------ + +When downloading using HTTP, the library sends only one HTTP request to the server and receives only one HTTP response. + +When downloading using HTTPS with an nRF91 Series device, it is carried out through `Content-Range requests (IETF RFC 7233)`_ due to memory constraints that limit the maximum HTTPS message size to two kilobytes. +The library thus sends and receives as many requests and responses as the number of fragments that constitute the download. +For example, to download a file of 47 kilobytes with a fragment size of 2 kilobytes, a total of 24 HTTP GET requests are sent. +The download can also be carried out through fragments by specifying the :c:member:`downloader_host_cfg.range_override` field of the host configuration. + +CoAP and CoAPS (DTLS 1.2) +------------------------- + +The CoAP feature is disabled by default. +You can enable it using the :kconfig:option:`CONFIG_DOWNLOADER_TRANSPORT_COAP` Kconfig option. +When downloading from a CoAP server, the library uses the CoAP block-wise transfer. + +Configuration +************* + +The configuration of the library depends on the protocol you are using. + +Configuring HTTP and HTTPS (TLS 1.2) +==================================== + +Make sure that the buffer provided to the downloader is large enough to accommodate the entire HTTP header of the request. +Ensure that the values of the :kconfig:option:`CONFIG_DOWNLOADER_MAX_HOSTNAME_SIZE` and :kconfig:option:`CONFIG_DOWNLOADER_MAX_FILENAME_SIZE` Kconfig options are large enough for your host and filenames, respectively. + +When using HTTPS the application must provision the TLS credentials and pass the security tag to the library through the :c:struct:`downloader_host_cfg` structure. +To provision a TLS certificate to the modem, use :c:func:`modem_key_mgmt_write` and other :ref:`modem_key_mgmt` APIs. + +Configuring CoAP and CoAPS (DTLS 1.2) +===================================== + +Make sure the buffer provided to the downloader is large enough to accommodate the entire CoAP header and the CoAP block. +The CoAP block size is provided by the :kconfig:option:`CONFIG_DOWNLOADER_COAP_BLOCK_SIZE_CHOICE` Kconfig option. +Ensure that the values of the :kconfig:option:`CONFIG_DOWNLOADER_MAX_HOSTNAME_SIZE` and :kconfig:option:`CONFIG_DOWNLOADER_MAX_FILENAME_SIZE` Kconfig options are large enough for your host and filenames, respectively. + +When using CoAPS the application must provision the TLS credentials and pass the security tag to the library through the :c:struct:`downloader_host_cfg` structure. + +When you have modem firmware v1.3.5 or newer, you can use the DTLS Connection Identifier feature in this library by setting the ``cid`` flag in the :c:struct:`downloader_host_cfg` structure. + +Usage +***** + +To initialize the library, call the :c:func:`downloader_init` function as follows: + +.. code-block:: c + + int err; + + static int dl_callback(const struct downloader_evt *event); + char dl_buf[2048]; + struct downloader dl; + struct downloader_cfg dl_cfg = { + .callback = dl_callback, + .buf = dl_buf, + .buf_size, + }; + + err = downloader_init(&dl, &dl_cfg); + if (err) { + printk("downloader init failed, err %d\n", err); + } + +To deinitialize the library, call the :c:func:`downloader_deinit` function as follows: + +.. code-block:: c + + int err; + struct downloader dl; + + /* downloader is initialized */ + + err = downloader_deinit(&dl); + if (err) { + printk("downloader deinit failed, err %d\n", err); + } + +This will free up the resources used by the library. + +The following snippet shows how to download a file using HTTPS: + +.. code-block:: c + + + int err; + int dl_res; + + static int dl_callback(const struct downloader_evt *event) { + switch (event->id) { + case DOWNLOADER_EVT_FRAGMENT: + printk("Received fragment, dataptr: %p, len %d\n", + event->fragment.buf, event->fragment.len); + return 0; + case DOWNLOADER_EVT_ERROR: + printk("downloader error: %d\n", event->error); + dl_res = event->error; + return 0; + case DOWNLOADER_EVT_DONE: + printk("downloader done\n"); + dl_res = 0; + return 0; + case DOWNLOADER_EVT_STOPPED: + printk("downloader stopped\n"); + k_sem_give(&dl_sem); + return 0; + case DOWNLOADER_EVT_DEINITIALIZED: + printk("downloader deinitialized\n"); + return 0; + } + } + + char dl_buf[2048]; + struct downloader dl; + struct downloader_cfg dl_cfg = { + .callback = dl_callback, + .buf = dl_buf, + .buf_size, + }; + + int sec_tags[] = {1, 2, 3}; + + struct downloader_host_cfg dl_host_cfg = { + .sec_tag_list = sec_tags, + .sec_tag_count = ARRAY_SIZE(sec_tags), + /* This will disconnect the downloader from the server when the download is complete */ + .keep_connection = false, + }; + + struct downloader_transport_http_cfg dl_transport_http_cfg = { + .sock_recv_timeo = 600000, + }; + + err = downloader_init(&dl, &dl_cfg); + if (err) { + printk("downloader init failed, err %d\n", err); + } + + err = downloader_transport_http_set_config(&dl, &dl_transport_http_cfg); + if (err) { + printk("failed to set http transport params failed, err %d\n", err); + } + + err = downloader_get(&dl, &dl_host_cfg, "https://myserver.com/path/to/file.txt"); + if (err) { + printk("downloader start failed, err %d\n", err); + } + + /* Wait for download to complete */ + k_sem_take(&dl_sem, K_FOREVER); + + err = downloader_deinit(&dl); + if (err) { + printk("downloader deinit failed, err %d\n", err); + } + +Limitations +*********** + +The library requires the host server to provide a Content-Range field in the HTTP GET response header when using HTTPS with the nRF91 Series devices. +If this header field is missing, the library logs the following error:: + + downloader: Server did not send "Content-Range" in response + + Due to internal limitations, maximum CoAP block size is 512 bytes. + +API documentation +***************** + +| Header file: :file:`include/downloader.h`, :file:`include/downloader_transport.h`, :file:`include/downloader_transport_http.h`, :file:`include/downloader_transpot_coap.h` +| Source files: :file:`subsys/net/lib/downloader/src/` + +.. doxygengroup:: downloader diff --git a/doc/nrf/libraries/networking/fota_download.rst b/doc/nrf/libraries/networking/fota_download.rst index 15d81950cc03..35e645a57a48 100644 --- a/doc/nrf/libraries/networking/fota_download.rst +++ b/doc/nrf/libraries/networking/fota_download.rst @@ -19,7 +19,7 @@ To start a FOTA download, provide the URL for the file that should be downloaded * ``file`` - It indicates the path to the file. For example, ``path/to/resource/file.bin``. -The FOTA library downloads the image using the :ref:`lib_download_client` library. +The FOTA library downloads the image using the :ref:`lib_downloader` library. After downloading the first fragment, it uses the :ref:`lib_dfu_target` library to identify the type of image that is being downloaded. Examples of image types are *modem upgrades* and upgrades handled by a *second-stage bootloader*. @@ -38,7 +38,7 @@ HTTPS downloads The FOTA download library is used in the :ref:`http_application_update_sample` sample. By default, the FOTA download library uses HTTP for downloading the firmware file. -To use HTTPS, apply the changes described in :ref:`the HTTPS section of the download client documentation ` to the library. +To use HTTPS, apply the changes described in :ref:`the HTTPS section of the Downloader library documentation ` to the library. Second-stage bootloader upgrades ******************************** diff --git a/doc/nrf/libraries/networking/nrf_cloud.rst b/doc/nrf/libraries/networking/nrf_cloud.rst index 602f7d12bbfe..4968e833bd3e 100644 --- a/doc/nrf/libraries/networking/nrf_cloud.rst +++ b/doc/nrf/libraries/networking/nrf_cloud.rst @@ -160,7 +160,7 @@ nRF Cloud FOTA enables the following additional features and libraries: * :kconfig:option:`CONFIG_FOTA_DOWNLOAD` enables :ref:`lib_fota_download` * :kconfig:option:`CONFIG_DFU_TARGET` enables :ref:`lib_dfu_target` -* :kconfig:option:`CONFIG_DOWNLOAD_CLIENT` enables :ref:`lib_download_client` +* :kconfig:option:`CONFIG_DOWNLOADER` enables :ref:`lib_downloader` * :kconfig:option:`CONFIG_FOTA_DOWNLOAD_PROGRESS_EVT` * :kconfig:option:`CONFIG_FOTA_PROGRESS_EVT_INCREMENT` * :kconfig:option:`CONFIG_REBOOT` diff --git a/doc/nrf/libraries/networking/nrf_cloud_pgps.rst b/doc/nrf/libraries/networking/nrf_cloud_pgps.rst index 0050157c2efa..b927a22dba6b 100644 --- a/doc/nrf/libraries/networking/nrf_cloud_pgps.rst +++ b/doc/nrf/libraries/networking/nrf_cloud_pgps.rst @@ -292,7 +292,7 @@ Dependencies This library uses the following |NCS| libraries: * :ref:`lib_date_time` -* :ref:`lib_download_client` +* :ref:`lib_downloader` * :ref:`modem_info_readme` * :ref:`lib_nrf_cloud` diff --git a/doc/nrf/links.txt b/doc/nrf/links.txt index 6f71ee44abae..ab7310b24ee1 100644 --- a/doc/nrf/links.txt +++ b/doc/nrf/links.txt @@ -569,6 +569,7 @@ .. _`LwM2M carrier library changelog for v1.9.1`: https://docs.nordicsemi.com/bundle/ncs-1.9.1/page/nrf/libraries/bin/lwm2m_carrier/CHANGELOG.html .. _`LwM2M carrier library changelog for v1.9.0`: https://docs.nordicsemi.com/bundle/ncs-1.9.0/page/nrf/libraries/bin/lwm2m_carrier/CHANGELOG.html +.. _`Migration guide for nRF Connect SDK v3.0.0`: https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/releases_and_maturity/migration/migration_guide_3.0.html .. _`Migration guide for nRF Connect SDK v2.9.0-nRF54H20-rc1`: https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/releases_and_maturity/migration/migration_guide_2.9.0-nrf54h20-rc1.html .. _`Migration guide for nRF Connect SDK v2.9.0`: https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/releases_and_maturity/migration/migration_guide_2.9.html .. _`Migration guide for nRF Connect SDK v2.8.0`: https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/releases_and_maturity/migration/migration_guide_2.8.html diff --git a/doc/nrf/releases_and_maturity/known_issues.rst b/doc/nrf/releases_and_maturity/known_issues.rst index dd7ffab778ab..defce861324c 100644 --- a/doc/nrf/releases_and_maturity/known_issues.rst +++ b/doc/nrf/releases_and_maturity/known_issues.rst @@ -3580,9 +3580,9 @@ NCSDK-11432: DFU: Erasing secondary slot returns error response NCSDK-6238: Socket API calls might hang when using Download client When using the :ref:`lib_download_client` library with HTTP (without TLS), the application might not process incoming fragments fast enough, which can starve the :ref:`nrfxlib:nrf_modem` buffers and make calls to the Modem library hang. - Samples and applications that are affected include those that use :ref:`lib_download_client` to download files through HTTP, or those that use :ref:`lib_fota_download` with modem updates enabled. + Samples and applications that are affected include those that use the :ref:`lib_download_client` library to download files through HTTP, or those that use :ref:`lib_fota_download` with modem updates enabled. - **Workaround:** Set :kconfig:option:`CONFIG_DOWNLOAD_CLIENT_RANGE_REQUESTS`. + **Workaround:** Set :kconfig:option:`CONFIG_DOWNLOAD_CLIENT_RANGE_REQUESTS` with the :ref:`lib_download_client` library. .. rst-class:: v1-1-0 @@ -3594,7 +3594,7 @@ Jobs not received after reset .. rst-class:: v2-6-2 v2-6-1 v2-6-0 v2-5-3 v2-5-2 v2-5-1 v2-5-0 NCSDK-24305: fota_download library sends FOTA_DOWNLOAD_EVT_FINISHED when unable to connect - The :ref:`lib_download_client` library does not resume a download if the device cannot connect to a target server. + The :ref:`lib_download_client` library do not resume a download if the device cannot connect to a target server. This causes the :ref:`lib_fota_download` library to incorrectly assume that the download has completed. **Workaround:** Set the :kconfig:option:`CONFIG_FOTA_SOCKET_RETRIES` Kconfig option to ``0``. diff --git a/doc/nrf/releases_and_maturity/migration/migration_guide_3.0.rst b/doc/nrf/releases_and_maturity/migration/migration_guide_3.0.rst new file mode 100644 index 000000000000..31d4440bf3fc --- /dev/null +++ b/doc/nrf/releases_and_maturity/migration/migration_guide_3.0.rst @@ -0,0 +1,219 @@ +.. _migration_3.0: + +Migration guide for |NCS| v3.0.0 (Working draft) +################################################ + +.. contents:: + :local: + :depth: 3 + +This document describes the changes required or recommended when migrating your application from |NCS| v2.9.0 to |NCS| v3.0.0. + +.. HOWTO + + Add changes in the following format: + + Component (for example, application, sample or libraries) + ********************************************************* + + .. toggle:: + + * Change1 and description + * Change2 and description + +.. _migration_3.0_required: + +Required changes +**************** + +The following changes are mandatory to make your application work in the same way as in previous releases. + +|no_changes_yet_note| + +Samples and applications +======================== + +This section describes the changes related to samples and applications. + +|no_changes_yet_note| + +Libraries +========= + +This section describes the changes related to libraries. + +|no_changes_yet_note| + +.. _migration_3.0_recommended: + +Recommended changes +******************* + +The following changes are recommended for your application to work optimally after the migration. + +Samples and applications +======================== + +This section describes the changes related to samples and applications. + +|no_changes_yet_note| + +Libraries +========= + +This section describes the changes related to libraries. + +Download client +--------------- + +.. toggle:: + + * The :ref:`lib_download_client` library has been deprecated in favor of the :ref:`lib_downloader` library and will be removed in a future |NCS| release. + + You can follow this guide to migrate your application to use the :ref:`lib_downloader` library. + This will reduce the footprint of the application and will decrease memory requirements on the heap. + + To replace :ref:`lib_download_client` with the :ref:`lib_downloader`, complete the following steps. + + 1. Kconfig options: + + * Replace: + + * The :kconfig:option:`CONFIG_DOWNLOAD_CLIENT` Kconfig option with the :kconfig:option:`CONFIG_DOWNLOADER` Kconfig option. + * The :kconfig:option:`CONFIG_DOWNLOAD_CLIENT_MAX_HOSTNAME_SIZE` Kconfig option with the :kconfig:option:`CONFIG_DOWNLOADER_MAX_HOSTNAME_SIZE` Kconfig option. + * The :kconfig:option:`CONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE` Kconfig option with the :kconfig:option:`CONFIG_DOWNLOADER_MAX_FILENAME_SIZE` Kconfig option. + * The :kconfig:option:`CONFIG_DOWNLOAD_CLIENT_STACK_SIZE` Kconfig option with the :kconfig:option:`CONFIG_DOWNLOADER_STACK_SIZE` Kconfig option. + * The :kconfig:option:`CONFIG_DOWNLOAD_CLIENT_SHELL` Kconfig option with the :kconfig:option:`CONFIG_DOWNLOADER_SHELL` Kconfig option. + * The :kconfig:option:`CONFIG_DOWNLOAD_CLIENT_TCP_SOCK_TIMEO_MS` Kconfig option with the :kconfig:option:`CONFIG_DOWNLOADER_HTTP_TIMEO_MS` Kconfig option. + * The :kconfig:option:`CONFIG_DOWNLOAD_CLIENT_COAP_MAX_RETRANSMIT_REQUEST_COUNT` Kconfig option with the :kconfig:option:`CONFIG_DOWNLOADER_COAP_MAX_RETRANSMIT_REQUEST_COUNT` Kconfig option. + * The :kconfig:option:`CONFIG_DOWNLOAD_CLIENT_COAP_BLOCK_SIZE` Kconfig option with the :kconfig:option:`CONFIG_DOWNLOADER_COAP_BLOCK_SIZE_512` Kconfig option. + + * Remove: + + * The :kconfig:option:`CONFIG_DOWNLOAD_CLIENT_BUF_SIZE` Kconfig option. + * The :kconfig:option:`CONFIG_DOWNLOAD_CLIENT_HTTP_FRAG_SIZE` Kconfig option. + * The :kconfig:option:`CONFIG_DOWNLOAD_CLIENT_RANGE_REQUESTS` Kconfig option. + * The :kconfig:option:`CONFIG_DOWNLOAD_CLIENT_CID` Kconfig option. + + * Add: + + * The :kconfig:option:`CONFIG_DOWNLOADER_TRANSPORT_COAP` Kconfig option to enable CoAP support. + * The :kconfig:option:`CONFIG_NET_IPV4` Kconfig option to enable IPv4 support. + * The :kconfig:option:`CONFIG_NET_IPV6` Kconfig option to enable IPv6 support. + + #. Replace header files: + + * Remove: + + .. code-block:: C + + #include + + * Add: + + .. code-block:: C + + #include + + #. Replace download client initialization: + + * Remove: + + .. code-block:: C + + static struct download_client dlc; + static int callback(const struct download_client_evt *event); + + download_client_init(&dlc, callback) + + * Add: + + .. code-block:: C + + static struct downloader dl; + static int callback(const struct downloader_evt *event); + static char dl_buf[2048]; /* Use buffer size set by CONFIG_DOWNLOAD_CLIENT_BUF_SIZE previously */ + static struct downloader_cfg dl_cfg = { + .callback = callback, + .buf = dl_buf, + .buf_size = sizeof(dl_buf), + }; + + downloader_init(&dl, &dl_cfg); + + #. Update download client callback: + + * Replace: + + * :c:enumerator:`DOWNLOAD_CLIENT_EVT_FRAGMENT` event with :c:enumerator:`DOWNLOADER_EVT_FRAGMENT`. + * :c:enumerator:`DOWNLOAD_CLIENT_EVT_ERROR` event with :c:enumerator:`DOWNLOADER_EVT_ERROR`. + * :c:enumerator:`DOWNLOAD_CLIENT_EVT_DONE` event with :c:enumerator:`DOWNLOADER_EVT_DONE`. + + * Remove: + + * :c:enumerator:`DOWNLOAD_CLIENT_EVT_CLOSED` event. + + * Add: + + * :c:enumerator:`DOWNLOADER_EVT_STOPPED` event. + * :c:enumerator:`DOWNLOADER_EVT_DEINITIALIZED` event. + + #. Server connect and disconnect: + + * The :c:func:`download_client_disconnect` function is not ported to the new downloader. + The downloader is expected to connect when the download begins. + If the ``keep_connection`` flag is set in the host configuration the connection persists after the download completes or is aborted by the :c:func:`downloader_cancel` function. + In this case, the downloader is disconnected when it is deinitialized by the :c:func:`downloader_deinit` function. + + + #. Replace file download: + + We show the changes for the :c:func:`download_client_start` function here, though the required work is + similar to the :c:func:`download_client_get` function. + + * Remove: + + .. code-block:: C + + int err; + const struct download_client_cfg dlc_config = { + ... + }; + + err = download_client_set_host(&dlc, dl_host, &dlc_config); + + err = download_client_start(&dlc, dl_file, offset); + + * Add: + + .. code-block:: C + + /* Note: All configuration of the downloader is done through the config structs. + * The downloader struct should not be modified by the application. + */ + + static struct downloader_host_cfg dl_host_cfg = { + ... + /* Note: + * .frag_size_override is replaced by .range_override. + * .set_tls_hostname is replaced by .set_native_tls. + * dlc.close_when_done is moved here and inverted(.keep_connection). + * Set .cid if CONFIG_DOWNLOAD_CLIENT_CID was enabled in the download client. + */ + }; + + int err = downloader_get_with_host_and_file(&dl, &dl_host_cfg, dl_host, dl_file, offset); + + .. note:: + The new downloader has an API to download the file using the URI directly. + + #. [optional] Deinitialize the downloader after use: + + The new downloader can be deinitialized to free its resources. + If another download is required later on, a new downloader instance needs to be initialized. + + * Add: + + .. code-block:: C + + err = downloader_deinit(&dl); diff --git a/doc/nrf/releases_and_maturity/releases/release-notes-2.4.0.rst b/doc/nrf/releases_and_maturity/releases/release-notes-2.4.0.rst index eb5bd677ee93..5000c21013f2 100644 --- a/doc/nrf/releases_and_maturity/releases/release-notes-2.4.0.rst +++ b/doc/nrf/releases_and_maturity/releases/release-notes-2.4.0.rst @@ -818,7 +818,7 @@ Libraries for networking * Updated: - * The ``download_client_connect`` function has been refactored to :c:func:`download_client_set_host` and made it non-blocking. + * The :c:func:`download_client_connect` function has been refactored to :c:func:`download_client_set_host` and made it non-blocking. * The configuration from one security tag to a list of security tags. * The library reports error ``ERANGE`` when HTTP range is requested but not supported by server. diff --git a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst index 28884b4b8c66..213e9af3e696 100644 --- a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst +++ b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst @@ -175,7 +175,7 @@ Machine learning Asset Tracker v2 ---------------- -|no_changes_yet_note| +* Updated the application to use the :ref:`lib_downloader` library instead of the deprecated :ref:`lib_download_client` library. Connectivity Bridge ------------------- @@ -214,7 +214,7 @@ nRF Machine Learning (Edge Impulse) Serial LTE modem ---------------- -|no_changes_yet_note| +* Updated the application to use the :ref:`lib_downloader` library instead of the deprecated :ref:`lib_download_client` library. Thingy:53: Matter weather station --------------------------------- @@ -278,6 +278,18 @@ Bluetooth Mesh samples Cellular samples ---------------- +* Updated the following samples to use the :ref:`lib_downloader` library instead of the :ref:`lib_download_client` library: + + * :ref:`http_application_update_sample` + * :ref:`http_modem_delta_update_sample` + * :ref:`http_modem_full_update_sample` + * :ref:`location_sample` + * :ref:`lwm2m_carrier` + * :ref:`lwm2m_client` + * :ref:`modem_shell_application` + * :ref:`nrf_cloud_multi_service` + * :ref:`nrf_cloud_rest_fota` + * :ref:`modem_shell_application` sample: * Removed the ``CONFIG_MOSH_LINK`` Kconfig option. @@ -326,7 +338,11 @@ Matter samples Networking samples ------------------ -|no_changes_yet_note| +* Updated the following samples to use the :ref:`lib_downloader` library instead of the :ref:`lib_download_client` library: + + * :ref:`aws_iot` + * :ref:`azure_iot_hub` + * :ref:`download_sample` NFC samples ----------- @@ -416,7 +432,9 @@ This section provides detailed lists of changes by :ref:`library `. Binary libraries ---------------- -|no_changes_yet_note| +* :ref:`liblwm2m_carrier_readme` library: + + * Updated the glue to use the :ref:`lib_downloader` library instead of the deprecated :ref:`lib_download_client` library. Bluetooth libraries and services -------------------------------- @@ -463,7 +481,17 @@ Multiprotocol Service Layer libraries Libraries for networking ------------------------ -|no_changes_yet_note| +* Added the :ref:`lib_downloader` library. +* Deprecated the :ref:`lib_download_client` library. + See the :ref:`migration guide ` for recommended changes. + +* Updated the following libraries to use the :ref:`lib_downloader` library instead of the :ref:`lib_download_client` library: + + * :ref:`lib_nrf_cloud` + * :ref:`lib_aws_fota` + * :ref:`lib_azure_fota` + * :ref:`lib_fota_download` + Libraries for NFC ----------------- diff --git a/drivers/sensor/sensor_sim/sensor_sim.c b/drivers/sensor/sensor_sim/sensor_sim.c index e1b256335534..0e83f2097e07 100644 --- a/drivers/sensor/sensor_sim/sensor_sim.c +++ b/drivers/sensor/sensor_sim/sensor_sim.c @@ -42,7 +42,7 @@ struct sensor_sim_data { sensor_trigger_handler_t drdy_handler; struct sensor_trigger drdy_trigger; - K_THREAD_STACK_MEMBER(thread_stack, + K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_SENSOR_SIM_THREAD_STACK_SIZE); struct k_thread thread; struct gpio_callback gpio_cb; diff --git a/include/net/download_client.h b/include/net/download_client.h index 84e5a7953099..82d057702193 100644 --- a/include/net/download_client.h +++ b/include/net/download_client.h @@ -143,8 +143,7 @@ struct download_client_cfg { * * @return Zero to continue the download, non-zero otherwise. */ -typedef int (*download_client_callback_t)( - const struct download_client_evt *event); +typedef int (*download_client_callback_t)(const struct download_client_evt *event); /** * @brief Download client instance. @@ -210,8 +209,7 @@ struct download_client { struct k_sem wait_for_download; /* Internal thread stack. */ - K_THREAD_STACK_MEMBER(thread_stack, - CONFIG_DOWNLOAD_CLIENT_STACK_SIZE); + K_THREAD_STACK_MEMBER(thread_stack, CONFIG_DOWNLOAD_CLIENT_STACK_SIZE); /** Event handler. */ download_client_callback_t callback; @@ -275,8 +273,7 @@ int download_client_set_host(struct download_client *client, const char *host, * * @retval int Zero on success, a negative error code otherwise. */ -int download_client_start(struct download_client *client, const char *file, - size_t from); +int download_client_start(struct download_client *client, const char *file, size_t from); /** * @brief Retrieve the size of the file being downloaded, in bytes. diff --git a/include/net/downloader.h b/include/net/downloader.h new file mode 100644 index 000000000000..3a731acf8839 --- /dev/null +++ b/include/net/downloader.h @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/** + * @file downloader.h + * + * @defgroup downloader Downloader + * @{ + * @brief Client for downloading a file. + * + * @details The downloader provides APIs for: + * - downloading a file from the server, + * - receiving asynchronous event notifications on the download status. + */ + +#ifndef __DOWNLOADER_H__ +#define __DOWNLOADER_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Downloader event IDs. + */ +enum downloader_evt_id { + /** + * Event contains a fragment of data received from the server. + * When using range requests the amount of data per fragment may be less than the + * range requested. + * The application may return any non-zero value to stop the download. + */ + DOWNLOADER_EVT_FRAGMENT, + /** + * An error has occurred during download and + * the connection to the server has been lost. + * + * Error reason may be one of the following: + * - -ECONNRESET: Socket error, peer closed connection. + * - -ECONNREFUSED: Socket error, connection refused by server. + * - -ENETDOWN: Socket error, network down. + * - -ETIMEDOUT: Socket error, connection timed out. + * - -EHOSTDOWN: Host went down during download. + * - -EBADMSG: HTTP response header not as expected. + * - -ERANGE: HTTP response does not support range requests. + * - -E2BIG: HTTP response header could not fit in buffer. + * - -EPROTONOSUPPORT: Protocol is not supported. + * - -EINVAL: Invalid configuration. + * - -EAFNOSUPPORT: Unsupported address family (IPv4/IPv6). + * - -EHOSTUNREACH: Failed to resolve the target address. + * + * In case of @c ECONNRESET errors, 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. + * On any other error code than @c ECONNRESET, the downloader will not attempt to reconnect + * and will ignore the return value. + * + * In case the download is stopped or completed, and the + * @c downloader_host_cfg.keep_connection flag is set, the downloader will stay + * connected to the server. If a new download is initiated towards a different server, the + * current connection is closed and the downloader will connect to the new server. The + * connection can be closed by deinitializing the downloader, which will also free its + * resources. + * + * If the @c downloader_host_cfg.keep_connection flag is not set, the downloader + * will automatically close the connection. The application should wait for the + * @c DOWNLOADER_EVT_STOPPED event before attempting another download. + */ + DOWNLOADER_EVT_ERROR, + /** Download complete. Downloader is ready for new download. */ + DOWNLOADER_EVT_DONE, + /** Download has been stopped. Downloader is ready for new download. */ + DOWNLOADER_EVT_STOPPED, + /** Downloader deinitialized. Memory can be freed. */ + DOWNLOADER_EVT_DEINITIALIZED, +}; + +/** + * @brief Downloader data fragment. + */ +struct downloader_fragment { + /** Fragment buffer. */ + const void *buf; + /** Length of fragment. */ + size_t len; +}; + +/** + * @brief Downloader event. + */ +struct downloader_evt { + /** Event ID. */ + enum downloader_evt_id id; + + union { + /** Error cause. */ + int error; + /** Fragment data. */ + struct downloader_fragment fragment; + }; +}; + +/** + * @brief Downloader asynchronous event handler. + * + * Through this callback, the application receives events, such as + * download of a fragment, download completion, or errors. + * + * On a @c DOWNLOADER_EVT_ERROR event with error @c ECONNRESET, + * returning zero from the callback will let the library attempt + * to reconnect to the server and continue the download. + * Otherwise, the callback may return any non-zero value + * to stop the download. On any other error code than @c ECONNRESET, the downloader + * will not attempt to reconnect and will ignore the return value. + * To resume the download, use @ref downloader_get(). + * + * @param[in] event The event. + * + * @return Zero to continue the download, non-zero otherwise. + */ +typedef int (*downloader_callback_t)(const struct downloader_evt *event); + +/** + * @brief Downloader configuration options. + */ +struct downloader_cfg { + /** Event handler. */ + downloader_callback_t callback; + /** Downloader buffer. */ + char *buf; + /** Downloader buffer size. */ + size_t buf_size; +}; + +/** + * @brief Downloader host configuration options. + */ +struct downloader_host_cfg { + /** + * TLS security tag list. + * Pass NULL to disable TLS. + * The list must be kept in scope while download is going on. + */ + const int *sec_tag_list; + /** + * Number of TLS security tags in list. + * Set to 0 to disable TLS. + */ + uint8_t sec_tag_count; + /** + * PDN ID to be used for the download. + * Zero is the default PDN. + */ + uint8_t pdn_id; + /** + * Range override. + * Request a number of bytes from the server at a time. + * 0 disables the range override, and the downloader will ask for the whole file. + * The nRF91 series has a limitation of decoding ~2k of data at once when using TLS, hence + * range override will be used in this case regardless of the value here. + */ + size_t range_override; + /** Use native TLS. */ + bool set_native_tls; + /** + * Keep connection to server when done. + * Server is disconnected if a file is requested from another server, and when the + * downloader is deinitialized. + */ + bool keep_connection; + /** + * Enable DTLS connection identifier (CID) feature. + * This option requires modem firmware version >= 1.3.5. + */ + bool cid; + /** + * Address family to be used for the download, @c AF_INET6 or @c AF_INET. + * Set to @c AF_UNSPEC (0) to fallback to @c AF_INET if @c AF_INET6 does not work. + */ + int family; +}; + +/** + * @brief Downloader internal state. + */ +enum downloader_state { + DOWNLOADER_DEINITIALIZED, + DOWNLOADER_IDLE, + DOWNLOADER_CONNECTING, + DOWNLOADER_CONNECTED, + DOWNLOADER_DOWNLOADING, + DOWNLOADER_STOPPING, + DOWNLOADER_DEINITIALIZING, +}; + +/** + * @brief Downloader instance. + * + * Members are set internally by the downloader. + */ +struct downloader { + /** Downloader configuration options. */ + struct downloader_cfg cfg; + /** Host configuration options. */ + struct downloader_host_cfg host_cfg; + /** Host name, null-terminated. */ + char hostname[CONFIG_DOWNLOADER_MAX_HOSTNAME_SIZE]; + /** File name, null-terminated. */ + char file[CONFIG_DOWNLOADER_MAX_FILENAME_SIZE]; + /** Size of the file being downloaded, in bytes. */ + size_t file_size; + /** Download progress, in number of bytes downloaded. */ + size_t progress; + /** Buffer offset. */ + size_t buf_offset; + /** Flag to signal that the download is complete. */ + bool complete; + /** + * Downloader transport, http, CoAP, MQTT, ... + * Store a pointer to the selected transport per downloader instance to avoid looking it up + * each call. + */ + const struct dl_transport *transport; + /** Transport parameters. */ + uint8_t transport_internal[CONFIG_DOWNLOADER_TRANSPORT_PARAMS_SIZE]; + + /** Ensure that thread is ready for download. */ + struct k_sem event_sem; + /** Protect shared variables. */ + struct k_mutex mutex; + /** Downloader state. */ + enum downloader_state state; + /** Internal download thread. */ + struct k_thread thread; + /** Internal thread ID. */ + k_tid_t tid; + /** Internal thread stack. */ + K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_DOWNLOADER_STACK_SIZE); +}; + +/** + * @brief Initialize the downloader. + * + * @param[in] dl Downloader instance. + * @param[in] cfg Downloader configuration options. + * + * @return Zero on success, otherwise a negative error code. + */ +int downloader_init(struct downloader *dl, struct downloader_cfg *cfg); + +/** + * @brief Deinitialize the downloader. + * + * @param[in] dl Downloader instance. + * + * @return Zero on success. + */ +int downloader_deinit(struct downloader *dl); + +/** + * @brief Download a file asynchronously. + * + * This initiates an asynchronous connect-download-disconnect sequence to the target + * host. + * + * Downloads are handled one at a time. If previous download is not finished + * this returns -EALREADY. + * + * @param[in] dl Downloader instance. + * @param[in] host_cfg Host configuration options. + * @param[in] url URL of the host to connect to. + * Can include scheme, port number and full file path, defaults to + * HTTP or HTTPS if no scheme is provided. + * @param[in] from Offset from where to resume the download, + * or zero to download from the beginning. + * + * @return Zero on success, a negative error code otherwise. + */ +int downloader_get(struct downloader *dl, const struct downloader_host_cfg *host_cfg, + const char *url, size_t from); + +/** + * @brief Download a file asynchronously with host and file as separate parameters. + * + * This initiates an asynchronous connect-download-disconnect sequence to the target + * host. + * + * Downloads are handled one at a time. If previous download is not finished, + * this returns -EALREADY. + * + * @param[in] dl Downloader instance. + * @param[in] host_cfg Host configuration options. + * @param[in] host URL of the host to connect to. + * Can include scheme and port number, defaults to + * HTTP or HTTPS if no scheme is provided. + * @param[in] file File to download + * @param[in] from Offset from where to resume the download, + * or zero to download from the beginning. + * + * @return Zero on success, a negative error code otherwise. + */ +int downloader_get_with_host_and_file(struct downloader *dl, + const struct downloader_host_cfg *host_cfg, + const char *host, const char *file, size_t from); + +/** + * @brief Cancel file download. + * + * Request downloader to stop the download. This does not block. + * If the @c downloader_host_cfg.keep_connection flag is set the downloader remains connected to + * the server. Else the downloader is disconnected. + * When the download is canceled an @c DOWNLOADER_EVT_STOPPED event is sent. + * + * @param[in] dl Downloader instance. + * + * @return Zero on success, a negative error code otherwise. + */ +int downloader_cancel(struct downloader *dl); + +/** + * @brief Retrieve the size of the file being downloaded, in bytes. + * + * The file size is only available after the download has begun. + * + * @param[in] dl Downloader instance. + * @param[out] size File size. + * + * @return Zero on success, a negative error code otherwise. + */ +int downloader_file_size_get(struct downloader *dl, size_t *size); + +/** + * @brief Retrieve the number of bytes downloaded so far. + * + * The progress is only available after the download has begun. + * + * @param[in] dl Downloader instance. + * @param[out] size Number of bytes downloaded so far. + * + * @return Zero on success, a negative error code otherwise. + */ +int downloader_downloaded_size_get(struct downloader *dl, size_t *size); + +#ifdef __cplusplus +} +#endif + +#endif /* __DOWNLOADER_H__ */ + +/**@} */ diff --git a/include/net/downloader_transport.h b/include/net/downloader_transport.h new file mode 100644 index 000000000000..cd35971bf5ef --- /dev/null +++ b/include/net/downloader_transport.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/** + * @file downloader_transport.h + * + * @defgroup downloader_transport Downloader transport + * @ingroup downloader + * @{ + * @brief Downloader transport definition. + */ + +#ifndef DOWNLOADER_TRANSPORT_H +#define DOWNLOADER_TRANSPORT_H + +#include + +/** + * @brief Transport data event callback. + * + * This function is called by the transport to notify the downloader of downloaded data. + * + * @param dl Downloader instance. + * @param data Downloaded data. + * @param len Length of downloaded data. + * + * @retval Zero if the fragment was accepted and the download can continue. + * @return Negative errno if the fragment was refused by the application and the download + * should be aborted. + */ +int dl_transport_evt_data(struct downloader *dl, void *data, size_t len); + +/** + * Downloader transport API + */ +struct dl_transport { + /** + * Parse protocol + * + * @param dl Downloader instance. + * @param uri URI + * + * @retval true if protocol is supported by the transport + * @retval false if protocol is not supported by the transport + */ + bool (*proto_supported)(struct downloader *dl, const char *uri); + /** + * Initialize DL transport + * + * @param dl Downloader instance. + * @param dl_host_cfg Host configuration. + * @param uri URI + * + * @returns 0 on success, negative error on failure. + */ + int (*init)(struct downloader *dl, struct downloader_host_cfg *dl_host_cfg, + const char *uri); + /** + * Deinitialize DL transport + * + * @param dl Downloader instance. + * + * @returns 0 on success, negative error on failure. + */ + int (*deinit)(struct downloader *dl); + /** + * Connect DL transport. + * + * Connection result is given by callback to @c dl_transport_event_connected. + * + * @param dl Downloader instance. + * + * @returns 0 on success, negative error on failure. + */ + int (*connect)(struct downloader *dl); + /** + * Close DL transport + * + * @param dl Downloader instance. + * + * @returns 0 on success, negative error on failure. + */ + int (*close)(struct downloader *dl); + /** + * Download data with DL transport + * + * @param dl Downloader instance. + * + * @returns 0 on success, negative error on failure. + * Return -ECONNRESET if the downloader can reconnect to resume the download. + */ + int (*download)(struct downloader *dl); +}; + +/** Downloader transport entry */ +struct dl_transport_entry { + /** Transport */ + const struct dl_transport *transport; +}; + +/** + * @brief Define a DL transport. + * + * @param entry The entry name. + * @param _transport The transport. + */ +#define DL_TRANSPORT(entry, _transport) \ + static STRUCT_SECTION_ITERABLE(dl_transport_entry, entry) = { \ + .transport = _transport, \ + } + +#endif /* DOWNLOADER_TRANSPORT_H */ + +/**@} */ diff --git a/include/net/downloader_transport_coap.h b/include/net/downloader_transport_coap.h new file mode 100644 index 000000000000..be4f8ea4b13a --- /dev/null +++ b/include/net/downloader_transport_coap.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/** + * @file downloader_transport_coap.h + * + * @defgroup downloader_transport_http Downloader HTTP transport + * @ingroup downloader_transport + * @{ + * @brief Downloader transport CoAP definitions. + */ + +#ifndef __DOWNLOADER_TRANSPORT_COAP_H +#define __DOWNLOADER_TRANSPORT_COAP_H + +#include +#include + +/** + * @brief CoAP transport configuration params. + */ +struct downloader_transport_coap_cfg { + /** CoAP block size. */ + enum coap_block_size block_size; + /** Max retransmission requests. */ + uint8_t max_retransmission; +}; + +/** + * @brief Set Downloader CoAP transport settings + * + * @param dl downloader instance + * @param cfg CoAP transport configuration + * + * @return Zero on success, negative errno on failure. + */ +int downloader_transport_coap_set_config(struct downloader *dl, + struct downloader_transport_coap_cfg *cfg); + +#endif /* __DOWNLOADER_TRANSPORT_COAP_H */ + +/**@} */ diff --git a/include/net/downloader_transport_http.h b/include/net/downloader_transport_http.h new file mode 100644 index 000000000000..f5c053775950 --- /dev/null +++ b/include/net/downloader_transport_http.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/** + * @file downloader_transport_http.h + * + * @defgroup downloader_transport_http Downloader HTTP transport + * @ingroup downloader_transport + * @{ + * @brief Downloader transport HTTP definitions. + */ + +#ifndef __DOWNLOADER_TRANSPORT_HTTP_H +#define __DOWNLOADER_TRANSPORT_HTTP_H + +#include + +/** + * @brief HTTP transport configuration params. + */ +struct downloader_transport_http_cfg { + /** Socket receive timeout in milliseconds */ + uint32_t sock_recv_timeo_ms; +}; + +/** + * @brief Set Downloader HTTP transport settings + * + * @param dl downloader instance + * @param cfg HTTP transport configuration + * + * @return Zero on success, negative errno on failure. + */ +int downloader_transport_http_set_config(struct downloader *dl, + struct downloader_transport_http_cfg *cfg); + +#endif /* __DOWNLOADER_TRANSPORT_HTTP_H */ + +/**@} */ diff --git a/include/net/fota_download.h b/include/net/fota_download.h index 505ae1fbb11d..6a45cbf18580 100644 --- a/include/net/fota_download.h +++ b/include/net/fota_download.h @@ -20,7 +20,7 @@ #include #include -#include +#include #include #ifdef __cplusplus @@ -139,8 +139,7 @@ int fota_download_init(fota_download_callback_t client_callback); * @param sec_tag_list Security tags that you want to use with HTTPS. Pass NULL to disable TLS. * @param sec_tag_count Number of TLS security tags in list. Pass 0 to disable TLS. * @param pdn_id Packet Data Network ID to use for the download, or 0 to use the default. - * @param fragment_size Fragment size to be used for the download. - * If 0, @kconfig{CONFIG_DOWNLOAD_CLIENT_HTTP_FRAG_SIZE} is used. + * @param fragment_size Fragment size to be used for the download. If 0, no fragmentation is used. * @param expected_type Type of firmware file to be downloaded and installed. * * @retval 0 If download has started successfully. @@ -172,14 +171,14 @@ int fota_download(const char *host, const char *file, const int *sec_tag_list, * download, both paths will be treated as upgradable bootloader slot 0 * and slot 1 binaries respectively, and only the binary corresponding to * the currently inactive bootloader slot will be selected and downloaded. -* See + * See * Secure Bootloader Chain Docs for details regarding the upgradable * bootloader slots. * @param sec_tag_list Security tags that you want to use with HTTPS. Pass NULL to disable TLS. * @param sec_tag_count Number of TLS security tags in list. Pass 0 to disable TLS. * @param pdn_id Packet Data Network ID to use for the download, or 0 to use the default. * @param fragment_size Fragment size to be used for the download. - * If 0, @kconfig{CONFIG_DOWNLOAD_CLIENT_HTTP_FRAG_SIZE} is used. + * If 0, no fragmentation is used. * * @retval 0 If download has started successfully. * @retval -EALREADY If download is already ongoing. @@ -205,7 +204,7 @@ int fota_download_any(const char *host, const char *file, const int *sec_tag_lis * @param sec_tag Security tag you want to use with HTTPS. Pass -1 to disable TLS. * @param pdn_id Packet Data Network ID to use for the download, or 0 to use the default. * @param fragment_size Fragment size to be used for the download. - * If 0, @kconfig{CONFIG_DOWNLOAD_CLIENT_HTTP_FRAG_SIZE} is used. + * If 0, no fragmentation is used. * * @retval 0 If download has started successfully. * @retval -EALREADY If download is already ongoing. @@ -230,7 +229,7 @@ int fota_download_start(const char *host, const char *file, int sec_tag, * @param sec_tag Security tag you want to use with HTTPS. Pass -1 to disable TLS. * @param pdn_id Packet Data Network ID to use for the download, or 0 to use the default. * @param fragment_size Fragment size to be used for the download. - * If 0, @kconfig{CONFIG_DOWNLOAD_CLIENT_HTTP_FRAG_SIZE} is used. + * If 0, no fragmentation is used. * @param expected_type Type of firmware file to be downloaded and installed. * * @retval 0 If download has started successfully. @@ -305,7 +304,7 @@ int fota_download_external_start(const char *host, const char *file, * @retval 0 If successful. * Otherwise, a (negative) error code is returned. */ -int fota_download_external_evt_handle(struct download_client_evt const *const evt); +int fota_download_external_evt_handle(struct downloader_evt const *const evt); #ifdef __cplusplus } diff --git a/include/net/nrf_cloud_pgps.h b/include/net/nrf_cloud_pgps.h index 86ad2f3ece30..d99bfab7af13 100644 --- a/include/net/nrf_cloud_pgps.h +++ b/include/net/nrf_cloud_pgps.h @@ -100,12 +100,11 @@ struct gps_pgps_request { * to nrf_cloud_pgps_process(). */ struct nrf_cloud_pgps_result { - /** User-provided buffer to hold download host name */ + /** User-provided buffer to hold download host */ char *host; /** Size of user-provided host buffer */ size_t host_sz; - - /** User-provided buffer to hold download path/file name */ + /** User-provided buffer to hold download path */ char *path; /** Size of user-provided path buffer */ size_t path_sz; diff --git a/lib/bin/lwm2m_carrier/Kconfig b/lib/bin/lwm2m_carrier/Kconfig index 70d7999e9ef1..85584d06d37c 100644 --- a/lib/bin/lwm2m_carrier/Kconfig +++ b/lib/bin/lwm2m_carrier/Kconfig @@ -26,9 +26,10 @@ menuconfig LWM2M_CARRIER depends on NET_SOCKETS depends on NET_SOCKETS_OFFLOAD # Networking NCS - depends on DOWNLOAD_CLIENT - depends on (DOWNLOAD_CLIENT_MAX_HOSTNAME_SIZE >= 64) - depends on (DOWNLOAD_CLIENT_MAX_FILENAME_SIZE >= 192) + depends on DOWNLOADER + depends on NET_IPV4 + depends on (DOWNLOADER_MAX_HOSTNAME_SIZE >= 64) + depends on (DOWNLOADER_MAX_FILENAME_SIZE >= 192) # AT libraries depends on AT_MONITOR depends on (AT_MONITOR_HEAP_SIZE >= 320) @@ -310,6 +311,15 @@ config LWM2M_CARRIER_FIRMWARE_DOWNLOAD_TIMEOUT PUSH delivery method of firmware images. Setting this to 0 will disable the use of this timer. +config LWM2M_CARRIER_FIRMWARE_DOWNLOAD_BUF_SIZE + int "Firmware download buffer size" + range 128 65535 + default 2048 + help + Size of the buffer used for the downloader library to download a new firmware image. + Must be large enough to hold the message used to request data from the server, e.g. a + HTTP header. + config LWM2M_CARRIER_AUTO_REGISTER bool "Auto registration on LTE Attach" default y diff --git a/lib/bin/lwm2m_carrier/include/lwm2m_os.h b/lib/bin/lwm2m_carrier/include/lwm2m_os.h index 40c419ff15b9..7971277ead08 100644 --- a/lib/bin/lwm2m_carrier/include/lwm2m_os.h +++ b/lib/bin/lwm2m_carrier/include/lwm2m_os.h @@ -447,7 +447,7 @@ void lwm2m_os_sms_client_deregister(int handle); * * @retval 0 If success. */ -int lwm2m_os_download_get(const char *host, const struct lwm2m_os_download_cfg *cfg, size_t from); +int lwm2m_os_download_get(const char *uri, const struct lwm2m_os_download_cfg *cfg, size_t from); /** * @brief Disconnect from the server. diff --git a/lib/bin/lwm2m_carrier/os/lwm2m_os.c b/lib/bin/lwm2m_carrier/os/lwm2m_os.c index 57a2145f4254..5e5d35a6b530 100644 --- a/lib/bin/lwm2m_carrier/os/lwm2m_os.c +++ b/lib/bin/lwm2m_carrier/os/lwm2m_os.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include @@ -412,49 +412,57 @@ void lwm2m_os_sms_client_deregister(int handle) } /* Download client module abstractions. */ +static char dl_buf[CONFIG_LWM2M_CARRIER_FIRMWARE_DOWNLOAD_BUF_SIZE]; +static int callback(const struct downloader_evt *event); -static struct download_client http_downloader; +static struct downloader_cfg dl_cfg = { + .callback = callback, + .buf = dl_buf, + .buf_size = sizeof(dl_buf), +}; + +static struct downloader http_downloader; static lwm2m_os_download_callback_t lwm2m_os_lib_callback; -int lwm2m_os_download_get(const char *host, const struct lwm2m_os_download_cfg *cfg, size_t from) +int lwm2m_os_download_get(const char *uri, const struct lwm2m_os_download_cfg *cfg, size_t from) { - struct download_client_cfg config = { + struct downloader_host_cfg dl_host_cfg = { .sec_tag_list = cfg->sec_tag_list, .sec_tag_count = cfg->sec_tag_count, .pdn_id = cfg->pdn_id, }; if (cfg->family == LWM2M_OS_PDN_FAM_IPV6) { - config.family = AF_INET6; + dl_host_cfg.family = AF_INET6; } else if (cfg->family == LWM2M_OS_PDN_FAM_IPV4) { - config.family = AF_INET; + dl_host_cfg.family = AF_INET; } - return download_client_get(&http_downloader, host, &config, NULL, from); + return downloader_get(&http_downloader, &dl_host_cfg, uri, from); } int lwm2m_os_download_disconnect(void) { - return download_client_disconnect(&http_downloader); + return downloader_cancel(&http_downloader); } -static void download_client_evt_translate(const struct download_client_evt *event, - struct lwm2m_os_download_evt *lwm2m_os_event) +static void downloader_evt_translate(const struct downloader_evt *event, + struct lwm2m_os_download_evt *lwm2m_os_event) { switch (event->id) { - case DOWNLOAD_CLIENT_EVT_FRAGMENT: + case DOWNLOADER_EVT_FRAGMENT: lwm2m_os_event->id = LWM2M_OS_DOWNLOAD_EVT_FRAGMENT; lwm2m_os_event->fragment.buf = event->fragment.buf; lwm2m_os_event->fragment.len = event->fragment.len; break; - case DOWNLOAD_CLIENT_EVT_DONE: + case DOWNLOADER_EVT_DONE: lwm2m_os_event->id = LWM2M_OS_DOWNLOAD_EVT_DONE; break; - case DOWNLOAD_CLIENT_EVT_ERROR: + case DOWNLOADER_EVT_ERROR: lwm2m_os_event->id = LWM2M_OS_DOWNLOAD_EVT_ERROR; lwm2m_os_event->error = event->error; break; - case DOWNLOAD_CLIENT_EVT_CLOSED: + case DOWNLOADER_EVT_STOPPED: lwm2m_os_event->id = LWM2M_OS_DOWNLOAD_EVT_CLOSED; break; default: @@ -462,11 +470,11 @@ static void download_client_evt_translate(const struct download_client_evt *even } } -static int callback(const struct download_client_evt *event) +static int callback(const struct downloader_evt *event) { struct lwm2m_os_download_evt lwm2m_os_event; - download_client_evt_translate(event, &lwm2m_os_event); + downloader_evt_translate(event, &lwm2m_os_event); return lwm2m_os_lib_callback(&lwm2m_os_event); } @@ -475,12 +483,12 @@ int lwm2m_os_download_init(lwm2m_os_download_callback_t lib_callback) { lwm2m_os_lib_callback = lib_callback; - return download_client_init(&http_downloader, callback); + return downloader_init(&http_downloader, &dl_cfg); } int lwm2m_os_download_file_size_get(size_t *size) { - return download_client_file_size_get(&http_downloader, size); + return downloader_file_size_get(&http_downloader, size); } bool lwm2m_os_uicc_bootstrap_is_enabled(void) diff --git a/samples/cellular/http_update/application_update/overlay-carrier.conf b/samples/cellular/http_update/application_update/overlay-carrier.conf index 36f0fd1dabd1..a78700c047ae 100644 --- a/samples/cellular/http_update/application_update/overlay-carrier.conf +++ b/samples/cellular/http_update/application_update/overlay-carrier.conf @@ -23,7 +23,7 @@ CONFIG_AT_MONITOR_HEAP_SIZE=320 CONFIG_HEAP_MEM_POOL_SIZE=4096 # Download client for DFU -CONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE=230 +CONFIG_DOWNLOADER_MAX_FILENAME_SIZE=230 # Non-volatile Storage CONFIG_NVS=y @@ -38,3 +38,6 @@ CONFIG_LWM2M_CARRIER_SETTINGS=n # DFU target library CONFIG_DFU_TARGET=y + +# IPv4 (IPv6 is enabled by default) +CONFIG_NET_IPV4=y diff --git a/samples/cellular/http_update/application_update/prj.conf b/samples/cellular/http_update/application_update/prj.conf index 82a74fd6a786..6c021fc1a8ec 100644 --- a/samples/cellular/http_update/application_update/prj.conf +++ b/samples/cellular/http_update/application_update/prj.conf @@ -12,6 +12,7 @@ CONFIG_NEWLIB_LIBC=y CONFIG_NETWORKING=y CONFIG_NET_SOCKETS=y CONFIG_NET_NATIVE=n +CONFIG_NET_IPV4=y # LTE link control CONFIG_LTE_LINK_CONTROL=y @@ -30,6 +31,7 @@ CONFIG_DK_LIBRARY=y # Heap and stacks CONFIG_HEAP_MEM_POOL_SIZE=2048 CONFIG_MAIN_STACK_SIZE=8192 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 # Image manager CONFIG_IMG_MANAGER=y @@ -45,8 +47,8 @@ CONFIG_GPIO=y CONFIG_FOTA_DOWNLOAD=y # Download client -CONFIG_DOWNLOAD_CLIENT=y -CONFIG_DOWNLOAD_CLIENT_STACK_SIZE=4096 +CONFIG_DOWNLOADER=y +CONFIG_DOWNLOADER_STACK_SIZE=4096 # DFU Target CONFIG_DFU_TARGET=y diff --git a/samples/cellular/http_update/application_update/src/main.c b/samples/cellular/http_update/application_update/src/main.c index cbcb5b36d143..164bb54855b4 100644 --- a/samples/cellular/http_update/application_update/src/main.c +++ b/samples/cellular/http_update/application_update/src/main.c @@ -367,7 +367,6 @@ static int update_download(void) file = CONFIG_DOWNLOAD_FILE_V2; #endif - /* Functions for getting the host and file */ err = fota_download_start(CONFIG_DOWNLOAD_HOST, file, SEC_TAG, 0, 0); if (err) { app_dfu_btn_irq_enable(); diff --git a/samples/cellular/http_update/modem_delta_update/prj.conf b/samples/cellular/http_update/modem_delta_update/prj.conf index 692e4f7450bb..42af07bc4404 100644 --- a/samples/cellular/http_update/modem_delta_update/prj.conf +++ b/samples/cellular/http_update/modem_delta_update/prj.conf @@ -8,6 +8,8 @@ CONFIG_NCS_SAMPLES_DEFAULTS=y CONFIG_REBOOT=y CONFIG_NEWLIB_LIBC=y +CONFIG_NET_IPV4=y + # Network CONFIG_NETWORKING=y CONFIG_NET_SOCKETS=y @@ -30,6 +32,7 @@ CONFIG_SHELL_CMD_BUFF_SIZE=128 # Heap and stacks CONFIG_HEAP_MEM_POOL_SIZE=2048 CONFIG_MAIN_STACK_SIZE=8192 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 # GPIO CONFIG_GPIO=y @@ -38,8 +41,8 @@ CONFIG_GPIO=y CONFIG_FOTA_DOWNLOAD=y # Download client -CONFIG_DOWNLOAD_CLIENT=y -CONFIG_DOWNLOAD_CLIENT_STACK_SIZE=4096 +CONFIG_DOWNLOADER=y +CONFIG_DOWNLOADER_STACK_SIZE=4096 # DFU Target CONFIG_DFU_TARGET=y diff --git a/samples/cellular/http_update/modem_delta_update/src/main.c b/samples/cellular/http_update/modem_delta_update/src/main.c index 5a5b27274e44..635a8fe183da 100644 --- a/samples/cellular/http_update/modem_delta_update/src/main.c +++ b/samples/cellular/http_update/modem_delta_update/src/main.c @@ -296,23 +296,22 @@ static int update_download(void) return false; } - if (is_test_firmware()) { - file = CONFIG_DOWNLOAD_FILE_FOTA_TEST_TO_BASE; - } else { - file = CONFIG_DOWNLOAD_FILE_BASE_TO_FOTA_TEST; - } - err = fota_download_init(fota_dl_handler); if (err) { printk("fota_download_init() failed, err %d\n", err); return err; } - /* Functions for getting the host and file */ + file = CONFIG_DOWNLOAD_FILE_BASE_TO_FOTA_TEST; + + if (is_test_firmware()) { + file = CONFIG_DOWNLOAD_FILE_FOTA_TEST_TO_BASE; + } + err = fota_download(CONFIG_DOWNLOAD_HOST, file, &sec_tag, sec_tag_count, 0, 0, DFU_TARGET_IMAGE_TYPE_MODEM_DELTA); if (err) { - printk("fota_download_any() failed, err %d\n", err); + printk("fota_download() failed, err %d\n", err); return err; } @@ -402,8 +401,10 @@ static void fota_work_cb(struct k_work *work) break; case UPDATE_APPLY: printk("Applying firmware update. This can take a while.\n"); + lte_lc_power_off(); /* Re-initialize the modem to apply the update. */ + err = nrf_modem_lib_shutdown(); if (err) { printk("Failed to shutdown modem, err %d\n", err); diff --git a/samples/cellular/http_update/modem_full_update/prj.conf b/samples/cellular/http_update/modem_full_update/prj.conf index 78b182f54532..c5f814c52ac6 100644 --- a/samples/cellular/http_update/modem_full_update/prj.conf +++ b/samples/cellular/http_update/modem_full_update/prj.conf @@ -8,6 +8,8 @@ CONFIG_NCS_SAMPLES_DEFAULTS=y CONFIG_REBOOT=y CONFIG_NEWLIB_LIBC=y +CONFIG_NET_IPV4=y + # Network CONFIG_NETWORKING=y CONFIG_NET_SOCKETS=y @@ -44,8 +46,8 @@ CONFIG_GPIO=y CONFIG_FOTA_DOWNLOAD=y # Download client -CONFIG_DOWNLOAD_CLIENT=y -CONFIG_DOWNLOAD_CLIENT_STACK_SIZE=4096 +CONFIG_DOWNLOADER=y +CONFIG_DOWNLOADER_STACK_SIZE=4096 # DFU Target CONFIG_DFU_TARGET=y diff --git a/samples/cellular/http_update/modem_full_update/src/main.c b/samples/cellular/http_update/modem_full_update/src/main.c index f026181879b5..adff1e3aaebe 100644 --- a/samples/cellular/http_update/modem_full_update/src/main.c +++ b/samples/cellular/http_update/modem_full_update/src/main.c @@ -277,38 +277,47 @@ static int apply_state(enum fota_state new_state) return 0; } -static int apply_fmfu_from_ext_flash(bool valid_init) +static int apply_fmfu_from_ext_flash(void) { int err; printk("Applying full modem firmware update from external flash\n"); - if (valid_init) { - err = nrf_modem_lib_shutdown(); - if (err != 0) { - printk("nrf_modem_lib_shutdown() failed: %d\n", err); - return err; - } + err = lte_lc_offline(); + if (err) { + printk("Failed to disconnect LTE."); + return err; + } + + err = nrf_modem_lib_shutdown(); + if (err != 0) { + printk("nrf_modem_lib_shutdown() failed: %d\n", err); + return err; } + err = nrf_modem_lib_bootloader_init(); if (err != 0) { printk("nrf_modem_lib_bootloader_init() failed: %d\n", err); - return err; + goto reinit; } err = fmfu_fdev_load(fmfu_buf, sizeof(fmfu_buf), flash_dev, 0); if (err != 0) { printk("fmfu_fdev_load failed: %d\n", err); - return err; + nrf_modem_lib_shutdown(); + goto reinit; } err = nrf_modem_lib_shutdown(); if (err != 0) { printk("nrf_modem_lib_shutdown() failed: %d\n", err); - return err; + goto reinit; } + printk("Modem firmware update completed, reinitializing in normal mode\n"); + +reinit: err = nrf_modem_lib_init(); if (err) { printk("Modem library initialization failed, err %d\n", err); @@ -323,11 +332,9 @@ static int apply_fmfu_from_ext_flash(bool valid_init) } } - printk("Modem firmware update completed.\n"); - current_version_display(); - return 0; + return err; } #if defined(CONFIG_USE_HTTPS) @@ -406,7 +413,8 @@ void fota_dl_handler(const struct fota_download_evt *evt) switch (evt->id) { case FOTA_DOWNLOAD_EVT_ERROR: printk("Received error from fota_download\n"); - /* Fallthrough */ + apply_state(CONNECTED); + break; case FOTA_DOWNLOAD_EVT_FINISHED: apply_state(UPDATE_PENDING); break; @@ -451,15 +459,14 @@ static int update_download(void) return err; } + file = CONFIG_DOWNLOAD_MODEM_0_FILE; + if (current_version_is_0()) { file = CONFIG_DOWNLOAD_MODEM_1_FILE; - } else { - file = CONFIG_DOWNLOAD_MODEM_0_FILE; } - /* Functions for getting the host and file */ err = fota_download(CONFIG_DOWNLOAD_HOST, file, &sec_tag, sec_tag_count, 0, 0, - DFU_TARGET_IMAGE_TYPE_FULL_MODEM); + DFU_TARGET_IMAGE_TYPE_FULL_MODEM); if (err != 0) { printk("fota_download() failed, err %d\n", err); return err; @@ -519,7 +526,7 @@ static void fota_work_cb(struct k_work *work) } break; case UPDATE_APPLY: - err = apply_fmfu_from_ext_flash(true); + err = apply_fmfu_from_ext_flash(); if (err) { printk("FMFU failed, err %d\n", err); } diff --git a/samples/cellular/location/overlay-pgps.conf b/samples/cellular/location/overlay-pgps.conf index cc116f12d89c..a6defeca321d 100644 --- a/samples/cellular/location/overlay-pgps.conf +++ b/samples/cellular/location/overlay-pgps.conf @@ -24,4 +24,4 @@ CONFIG_MPU_ALLOW_FLASH_WRITE=y CONFIG_HEAP_MEM_POOL_SIZE=8192 # Download client library stack size needs to be increased with P-GPS -CONFIG_DOWNLOAD_CLIENT_STACK_SIZE=1280 +CONFIG_DOWNLOADER_STACK_SIZE=1280 diff --git a/samples/cellular/lwm2m_carrier/prj.conf b/samples/cellular/lwm2m_carrier/prj.conf index 82e5c8fcb00d..86ad47c5be46 100644 --- a/samples/cellular/lwm2m_carrier/prj.conf +++ b/samples/cellular/lwm2m_carrier/prj.conf @@ -23,8 +23,8 @@ CONFIG_PDN=y CONFIG_SMS=y # Download client for DFU -CONFIG_DOWNLOAD_CLIENT=y -CONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE=230 +CONFIG_DOWNLOADER=y +CONFIG_DOWNLOADER_MAX_FILENAME_SIZE=230 # AT Monitor CONFIG_AT_MONITOR=y @@ -59,3 +59,6 @@ CONFIG_PM_PARTITION_SIZE_NVS_STORAGE=0 # DFU target library CONFIG_DFU_TARGET=y + +# IPv4 (IPv6 is enabled by default) +CONFIG_NET_IPV4=y diff --git a/samples/cellular/lwm2m_client/prj.conf b/samples/cellular/lwm2m_client/prj.conf index e6a3a9831fb2..3dee8af0e080 100644 --- a/samples/cellular/lwm2m_client/prj.conf +++ b/samples/cellular/lwm2m_client/prj.conf @@ -79,9 +79,8 @@ CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 CONFIG_AT_MONITOR_HEAP_SIZE=512 # Allow FOTA downloads using download-client -CONFIG_DOWNLOAD_CLIENT=y -CONFIG_DOWNLOAD_CLIENT_STACK_SIZE=4096 -CONFIG_DOWNLOAD_CLIENT_HTTP_FRAG_SIZE_1024=y +CONFIG_DOWNLOADER=y +CONFIG_DOWNLOADER_STACK_SIZE=4096 CONFIG_FOTA_DOWNLOAD=y # Application version diff --git a/samples/cellular/lwm2m_client/sample_description.rst b/samples/cellular/lwm2m_client/sample_description.rst index 6df5ad066162..b53d0b1fb917 100644 --- a/samples/cellular/lwm2m_client/sample_description.rst +++ b/samples/cellular/lwm2m_client/sample_description.rst @@ -834,7 +834,7 @@ This sample application uses the following |NCS| libraries and drivers: * :ref:`lib_dfu_target` * :ref:`lib_fmfu_fdev` * :ref:`lib_fota_download` -* :ref:`lib_download_client` +* :ref:`lib_downloader` It uses the following `sdk-nrfxlib`_ library: diff --git a/samples/cellular/modem_shell/overlay-carrier.conf b/samples/cellular/modem_shell/overlay-carrier.conf index b7aa9e7df79d..eb6c7c9af7df 100644 --- a/samples/cellular/modem_shell/overlay-carrier.conf +++ b/samples/cellular/modem_shell/overlay-carrier.conf @@ -16,7 +16,7 @@ CONFIG_MPU_ALLOW_FLASH_WRITE=y CONFIG_NVS=y CONFIG_NVS_LOG_LEVEL_OFF=y -CONFIG_DOWNLOAD_CLIENT=y +CONFIG_DOWNLOADER=y CONFIG_MODEM_KEY_MGMT=y @@ -34,3 +34,6 @@ CONFIG_IMG_ERASE_PROGRESSIVELY=n # DFU target library CONFIG_DFU_TARGET=y + +# IPv4 (IPv6 is enabled by default) +CONFIG_NET_IPV4=y diff --git a/samples/cellular/modem_shell/overlay-modem_fota_full.conf b/samples/cellular/modem_shell/overlay-modem_fota_full.conf index 59b537a33ce8..45a101218713 100644 --- a/samples/cellular/modem_shell/overlay-modem_fota_full.conf +++ b/samples/cellular/modem_shell/overlay-modem_fota_full.conf @@ -15,5 +15,5 @@ CONFIG_ZCBOR=y CONFIG_STREAM_FLASH_ERASE=y CONFIG_STREAM_FLASH=y CONFIG_FMFU_FDEV=y -CONFIG_DOWNLOAD_CLIENT_STACK_SIZE=2560 +CONFIG_DOWNLOADER_STACK_SIZE=2560 CONFIG_MBEDTLS_LEGACY_CRYPTO_C=y diff --git a/samples/cellular/modem_shell/prj.conf b/samples/cellular/modem_shell/prj.conf index fbd6bab1d0fa..ffb7c49a493e 100644 --- a/samples/cellular/modem_shell/prj.conf +++ b/samples/cellular/modem_shell/prj.conf @@ -144,7 +144,8 @@ CONFIG_FLASH=y CONFIG_REBOOT=y CONFIG_DFU_TARGET=y CONFIG_FOTA_DOWNLOAD=y -CONFIG_DOWNLOAD_CLIENT=y +CONFIG_DOWNLOADER=y +CONFIG_DOWNLOADER_SHELL=y # BOOTLOADER_MCUBOOT reduces usable flash size by half so it's disabled by default # This means application FOTA is disabled. Modem FOTA works without these. CONFIG_BOOTLOADER_MCUBOOT=n diff --git a/samples/cellular/modem_shell/src/fota/fota_shell.c b/samples/cellular/modem_shell/src/fota/fota_shell.c index 8dcf968e875f..c118c8932382 100644 --- a/samples/cellular/modem_shell/src/fota/fota_shell.c +++ b/samples/cellular/modem_shell/src/fota/fota_shell.c @@ -33,8 +33,7 @@ static int cmd_fota_download(const struct shell *shell, size_t argc, } else if (strcmp(argv[1], "au") == 0) { fota_server = fota_server_au; } else { - mosh_error("FOTA: Unknown server: %s", argv[1]); - return -EINVAL; + fota_server = argv[1]; } mosh_print("FOTA: Starting download..."); @@ -56,7 +55,9 @@ SHELL_STATIC_SUBCMD_SET_CREATE( sub_fota, SHELL_CMD_ARG( download, NULL, - " \nDownload and install a FOTA update. Available servers are \"eu\", \"us\", \"jpn\" and \"au\".", + " \nDownload and install a FOTA update. " + "Available servers are \"eu\", \"us\", \"jpn\" and \"au\"\n" + "You can also use the server address directly, e.g. http://example.com", cmd_fota_download, 3, 0), SHELL_SUBCMD_SET_END); diff --git a/samples/cellular/modem_shell/src/gnss/gnss.c b/samples/cellular/modem_shell/src/gnss/gnss.c index 467410c0d6db..96800569ae75 100644 --- a/samples/cellular/modem_shell/src/gnss/gnss.c +++ b/samples/cellular/modem_shell/src/gnss/gnss.c @@ -941,17 +941,20 @@ static void get_pgps_data_work_fn(struct k_work *work) err = nrf_cloud_rest_pgps_data_get(&rest_ctx, &request); #elif defined(CONFIG_NRF_CLOUD_COAP) struct nrf_cloud_pgps_result file_location = {0}; + static char host[64]; static char path[128]; memset(host, 0, sizeof(host)); memset(path, 0, sizeof(path)); + file_location.host = host; file_location.host_sz = sizeof(host); file_location.path = path; file_location.path_sz = sizeof(path); err = nrf_cloud_coap_pgps_url_get(&request, &file_location); + #endif if (err) { mosh_error("GNSS: Failed to get P-GPS data, error: %d", err); diff --git a/samples/cellular/nrf_cloud_multi_service/Kconfig b/samples/cellular/nrf_cloud_multi_service/Kconfig index e22f5e414eb4..acb98381f0c1 100644 --- a/samples/cellular/nrf_cloud_multi_service/Kconfig +++ b/samples/cellular/nrf_cloud_multi_service/Kconfig @@ -260,7 +260,7 @@ menuconfig COAP_FOTA select FOTA_DOWNLOAD_PROGRESS_EVT select IMG_ERASE_PROGRESSIVELY select DFU_TARGET - select DOWNLOAD_CLIENT + select DOWNLOADER select REBOOT select CJSON_LIB select SETTINGS diff --git a/samples/cellular/nrf_cloud_multi_service/prj.conf b/samples/cellular/nrf_cloud_multi_service/prj.conf index 67116b6f66aa..72eba8112941 100644 --- a/samples/cellular/nrf_cloud_multi_service/prj.conf +++ b/samples/cellular/nrf_cloud_multi_service/prj.conf @@ -115,11 +115,9 @@ CONFIG_SETTINGS_FCB=y CONFIG_FCB=y # Download Client - used by FOTA and PGPS -CONFIG_DOWNLOAD_CLIENT=y -CONFIG_DOWNLOAD_CLIENT_HTTP_FRAG_SIZE_1024=y -CONFIG_DOWNLOAD_CLIENT_STACK_SIZE=4096 -CONFIG_DOWNLOAD_CLIENT_BUF_SIZE=2300 -CONFIG_DOWNLOAD_CLIENT_MAX_HOSTNAME_SIZE=128 +CONFIG_DOWNLOADER=y +CONFIG_DOWNLOADER_STACK_SIZE=4096 +CONFIG_DOWNLOADER_MAX_HOSTNAME_SIZE=128 # Flash - Used by FOTA and PGPS CONFIG_FLASH=y diff --git a/samples/cellular/nrf_cloud_rest_fota/prj.conf b/samples/cellular/nrf_cloud_rest_fota/prj.conf index c0a47f95d996..81559cff9d2c 100644 --- a/samples/cellular/nrf_cloud_rest_fota/prj.conf +++ b/samples/cellular/nrf_cloud_rest_fota/prj.conf @@ -19,7 +19,7 @@ CONFIG_NRF_CLOUD_FOTA_POLL=y CONFIG_FOTA_DOWNLOAD=y CONFIG_FOTA_DOWNLOAD_PROGRESS_EVT=y CONFIG_DFU_TARGET=y -CONFIG_DOWNLOAD_CLIENT=y +CONFIG_DOWNLOADER=y # MCUBOOT CONFIG_BOOTLOADER_MCUBOOT=y diff --git a/samples/net/aws_iot/boards/nrf7002dk_nrf5340_cpuapp_ns.conf b/samples/net/aws_iot/boards/nrf7002dk_nrf5340_cpuapp_ns.conf index 3251b360651a..619550810638 100644 --- a/samples/net/aws_iot/boards/nrf7002dk_nrf5340_cpuapp_ns.conf +++ b/samples/net/aws_iot/boards/nrf7002dk_nrf5340_cpuapp_ns.conf @@ -97,10 +97,8 @@ CONFIG_FOTA_DOWNLOAD=y CONFIG_DFU_TARGET=y # Download client (needed by AWS FOTA) -CONFIG_DOWNLOAD_CLIENT=y -CONFIG_DOWNLOAD_CLIENT_STACK_SIZE=4096 -CONFIG_DOWNLOAD_CLIENT_BUF_SIZE=4096 -CONFIG_DOWNLOAD_CLIENT_HTTP_FRAG_SIZE_4096=y +CONFIG_DOWNLOADER=y +CONFIG_DOWNLOADER_STACK_SIZE=4096 # TLS credentials CONFIG_TLS_CREDENTIALS_BACKEND_PROTECTED_STORAGE=y diff --git a/samples/net/aws_iot/boards/nrf9151dk_nrf9151_ns.conf b/samples/net/aws_iot/boards/nrf9151dk_nrf9151_ns.conf index a4996fe39ef7..7edce8fef430 100644 --- a/samples/net/aws_iot/boards/nrf9151dk_nrf9151_ns.conf +++ b/samples/net/aws_iot/boards/nrf9151dk_nrf9151_ns.conf @@ -53,5 +53,5 @@ CONFIG_FOTA_DOWNLOAD=y CONFIG_DFU_TARGET=y # Download client (needed by AWS FOTA) -CONFIG_DOWNLOAD_CLIENT=y -CONFIG_DOWNLOAD_CLIENT_STACK_SIZE=4096 +CONFIG_DOWNLOADER=y +CONFIG_DOWNLOADER_STACK_SIZE=4096 diff --git a/samples/net/aws_iot/boards/nrf9160dk_nrf9160_ns.conf b/samples/net/aws_iot/boards/nrf9160dk_nrf9160_ns.conf index 131eed58a1de..739444cb4422 100644 --- a/samples/net/aws_iot/boards/nrf9160dk_nrf9160_ns.conf +++ b/samples/net/aws_iot/boards/nrf9160dk_nrf9160_ns.conf @@ -53,5 +53,5 @@ CONFIG_FOTA_DOWNLOAD=y CONFIG_DFU_TARGET=y # Download client (needed by AWS FOTA) -CONFIG_DOWNLOAD_CLIENT=y -CONFIG_DOWNLOAD_CLIENT_STACK_SIZE=4096 +CONFIG_DOWNLOADER=y +CONFIG_DOWNLOADER_STACK_SIZE=4096 diff --git a/samples/net/aws_iot/boards/nrf9161dk_nrf9161_ns.conf b/samples/net/aws_iot/boards/nrf9161dk_nrf9161_ns.conf index 7772d1e11098..93c2c3b655f4 100644 --- a/samples/net/aws_iot/boards/nrf9161dk_nrf9161_ns.conf +++ b/samples/net/aws_iot/boards/nrf9161dk_nrf9161_ns.conf @@ -53,5 +53,5 @@ CONFIG_FOTA_DOWNLOAD=y CONFIG_DFU_TARGET=y # Download client (needed by AWS FOTA) -CONFIG_DOWNLOAD_CLIENT=y -CONFIG_DOWNLOAD_CLIENT_STACK_SIZE=4096 +CONFIG_DOWNLOADER=y +CONFIG_DOWNLOADER_STACK_SIZE=4096 diff --git a/samples/net/aws_iot/boards/thingy91_nrf9160_ns.conf b/samples/net/aws_iot/boards/thingy91_nrf9160_ns.conf index fd574ed0e498..a8906323e49b 100644 --- a/samples/net/aws_iot/boards/thingy91_nrf9160_ns.conf +++ b/samples/net/aws_iot/boards/thingy91_nrf9160_ns.conf @@ -53,5 +53,5 @@ CONFIG_FOTA_DOWNLOAD=y CONFIG_DFU_TARGET=y # Download client (needed by AWS FOTA) -CONFIG_DOWNLOAD_CLIENT=y -CONFIG_DOWNLOAD_CLIENT_STACK_SIZE=4096 +CONFIG_DOWNLOADER=y +CONFIG_DOWNLOADER_STACK_SIZE=4096 diff --git a/samples/net/aws_iot/boards/thingy91x_nrf9151_ns.conf b/samples/net/aws_iot/boards/thingy91x_nrf9151_ns.conf index 96fa4982bc78..5ee1206c6079 100644 --- a/samples/net/aws_iot/boards/thingy91x_nrf9151_ns.conf +++ b/samples/net/aws_iot/boards/thingy91x_nrf9151_ns.conf @@ -53,5 +53,5 @@ CONFIG_FOTA_DOWNLOAD=y CONFIG_DFU_TARGET=y # Download client (needed by AWS FOTA) -CONFIG_DOWNLOAD_CLIENT=y -CONFIG_DOWNLOAD_CLIENT_STACK_SIZE=4096 +CONFIG_DOWNLOADER=y +CONFIG_DOWNLOADER_STACK_SIZE=4096 diff --git a/samples/net/azure_iot_hub/README.rst b/samples/net/azure_iot_hub/README.rst index 0b547a26d7fc..63a6370d51e1 100644 --- a/samples/net/azure_iot_hub/README.rst +++ b/samples/net/azure_iot_hub/README.rst @@ -224,17 +224,17 @@ If a new FOTA update is initiated, the console output is like this: azure_iot_hub_sample: AZURE_IOT_HUB_EVT_TWIN_RESULT_SUCCESS, ID: 140 azure_fota: Attempting to download firmware (version 'v0.0.2-dev') from example.com/firmware/app_update.bin - download_client: Downloading: firmware/app_update.bin [0] + downloader: Downloading: firmware/app_update.bin [0] azure_iot_hub_sample: AZURE_IOT_HUB_EVT_FOTA_START azure_iot_hub_sample: AZURE_IOT_HUB_EVT_TWIN_DESIRED_RECEIVED - download_client: Setting up TLS credentials, sec tag count 1 - download_client: Connecting to example.com + downloader: Setting up TLS credentials, sec tag count 1 + downloader: Connecting to example.com azure_iot_hub_sample: AZURE_IOT_HUB_EVT_TWIN_RESULT_SUCCESS, ID: 190 azure_iot_hub_sample: AZURE_IOT_HUB_EVT_TWIN_RESULT_SUCCESS, ID: 190 - download_client: Downloaded 1800/674416 bytes (0%) + downloader: Downloaded 1800/674416 bytes (0%) ... - download_client: Downloaded 674416/674416 bytes (100%) - download_client: Download complete + downloader: Downloaded 674416/674416 bytes (100%) + downloader: Download complete dfu_target_mcuboot: MCUBoot image-0 upgrade scheduled. Reset device to apply azure_iot_hub_sample: AZURE_IOT_HUB_EVT_FOTA_DONE azure_iot_hub_sample: The device will reboot in 5 seconds to apply update diff --git a/samples/net/azure_iot_hub/boards/nrf7002dk_nrf5340_cpuapp_ns.conf b/samples/net/azure_iot_hub/boards/nrf7002dk_nrf5340_cpuapp_ns.conf index 68dc74ed2614..6c86a1fb3f29 100644 --- a/samples/net/azure_iot_hub/boards/nrf7002dk_nrf5340_cpuapp_ns.conf +++ b/samples/net/azure_iot_hub/boards/nrf7002dk_nrf5340_cpuapp_ns.conf @@ -87,10 +87,8 @@ CONFIG_MCUBOOT_IMG_MANAGER=y CONFIG_IMG_MANAGER=y CONFIG_STREAM_FLASH=y CONFIG_IMG_ERASE_PROGRESSIVELY=y -CONFIG_DOWNLOAD_CLIENT=y -CONFIG_DOWNLOAD_CLIENT_STACK_SIZE=4096 -CONFIG_DOWNLOAD_CLIENT_BUF_SIZE=4096 -CONFIG_DOWNLOAD_CLIENT_HTTP_FRAG_SIZE_4096=y +CONFIG_DOWNLOADER=y +CONFIG_DOWNLOADER_STACK_SIZE=4096 # Enable external flash to host MCUBoot secondary partition CONFIG_SPI=y diff --git a/samples/net/azure_iot_hub/boards/nrf9151dk_nrf9151_ns.conf b/samples/net/azure_iot_hub/boards/nrf9151dk_nrf9151_ns.conf index b9c8237eecd3..5e3b80998bf5 100644 --- a/samples/net/azure_iot_hub/boards/nrf9151dk_nrf9151_ns.conf +++ b/samples/net/azure_iot_hub/boards/nrf9151dk_nrf9151_ns.conf @@ -39,5 +39,5 @@ CONFIG_STREAM_FLASH=y CONFIG_FLASH_MAP=y CONFIG_FLASH=y CONFIG_IMG_ERASE_PROGRESSIVELY=y -CONFIG_DOWNLOAD_CLIENT=y -CONFIG_DOWNLOAD_CLIENT_STACK_SIZE=4096 +CONFIG_DOWNLOADER=y +CONFIG_DOWNLOADER_STACK_SIZE=4096 diff --git a/samples/net/azure_iot_hub/boards/nrf9160dk_nrf9160_ns.conf b/samples/net/azure_iot_hub/boards/nrf9160dk_nrf9160_ns.conf index 8c90510ece39..8847cbfade9c 100644 --- a/samples/net/azure_iot_hub/boards/nrf9160dk_nrf9160_ns.conf +++ b/samples/net/azure_iot_hub/boards/nrf9160dk_nrf9160_ns.conf @@ -39,5 +39,5 @@ CONFIG_STREAM_FLASH=y CONFIG_FLASH_MAP=y CONFIG_FLASH=y CONFIG_IMG_ERASE_PROGRESSIVELY=y -CONFIG_DOWNLOAD_CLIENT=y -CONFIG_DOWNLOAD_CLIENT_STACK_SIZE=4096 +CONFIG_DOWNLOADER=y +CONFIG_DOWNLOADER_STACK_SIZE=4096 diff --git a/samples/net/azure_iot_hub/boards/nrf9161dk_nrf9161_ns.conf b/samples/net/azure_iot_hub/boards/nrf9161dk_nrf9161_ns.conf index 8c90510ece39..8847cbfade9c 100644 --- a/samples/net/azure_iot_hub/boards/nrf9161dk_nrf9161_ns.conf +++ b/samples/net/azure_iot_hub/boards/nrf9161dk_nrf9161_ns.conf @@ -39,5 +39,5 @@ CONFIG_STREAM_FLASH=y CONFIG_FLASH_MAP=y CONFIG_FLASH=y CONFIG_IMG_ERASE_PROGRESSIVELY=y -CONFIG_DOWNLOAD_CLIENT=y -CONFIG_DOWNLOAD_CLIENT_STACK_SIZE=4096 +CONFIG_DOWNLOADER=y +CONFIG_DOWNLOADER_STACK_SIZE=4096 diff --git a/samples/net/download/README.rst b/samples/net/download/README.rst index a9ccb6688de1..d490e8655fbc 100644 --- a/samples/net/download/README.rst +++ b/samples/net/download/README.rst @@ -1,14 +1,14 @@ .. _download_sample: -Download client -############### +Download +######## .. contents:: :local: :depth: 2 -The Download client sample demonstrates how to download a file from an HTTP or a CoAP server, with optional TLS or DTLS. -It uses the :ref:`lib_download_client` library. +The Download sample demonstrates how to download a file from an HTTP or a CoAP server, with optional TLS or DTLS. +It uses the :ref:`lib_downloader` library. .. |wifi| replace:: Wi-FiĀ® @@ -35,7 +35,7 @@ The sample then performs the following actions: 1. Establishes a connection to the network #. Optionally sets up the secure socket options -#. Uses the :ref:`lib_download_client` library to download a file from an HTTP server. +#. Uses the :ref:`lib_downloader` library to download a file from an HTTP server. Downloading from a CoAP server diff --git a/samples/net/download/prj.conf b/samples/net/download/prj.conf index 330d0f00479c..fe1e131b9cf2 100644 --- a/samples/net/download/prj.conf +++ b/samples/net/download/prj.conf @@ -4,8 +4,8 @@ # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause # -CONFIG_DOWNLOAD_CLIENT=y -CONFIG_DOWNLOAD_CLIENT_STACK_SIZE=4096 +CONFIG_DOWNLOADER=y +CONFIG_DOWNLOADER_STACK_SIZE=4096 # Networking CONFIG_NETWORKING=y diff --git a/samples/net/download/sample.yaml b/samples/net/download/sample.yaml index 3967f89e0fa4..d28def5fb33e 100644 --- a/samples/net/download/sample.yaml +++ b/samples/net/download/sample.yaml @@ -1,7 +1,7 @@ sample: name: Download sample tests: - sample.net.download_client: + sample.net.downloader: sysbuild: true build_only: true integration_platforms: @@ -13,13 +13,13 @@ tests: - nrf7002dk/nrf5340/cpuapp/ns - native_sim tags: ci_build sysbuild ci_samples_net - sample.net.download_client.ci: + sample.net.downloader.ci: sysbuild: true build_only: true extra_configs: - CONFIG_SHELL=y - CONFIG_COAP=y - - CONFIG_DOWNLOAD_CLIENT_SHELL=y + - CONFIG_DOWNLOADER_SHELL=y - CONFIG_SAMPLE_COMPUTE_HASH=y integration_platforms: - nrf9160dk/nrf9160/ns @@ -32,7 +32,7 @@ tests: - nrf9151dk/nrf9151/ns - nrf7002dk/nrf5340/cpuapp/ns tags: ci_build sysbuild ci_samples_net - sample.net.download_client.nrf54l15.wifi: + sample.net.downloader.nrf54l15.wifi: sysbuild: true tags: ci_build sysbuild ci_samples_net build_only: true diff --git a/samples/net/download/src/main.c b/samples/net/download/src/main.c index 0088d796607c..b6f870e56f10 100644 --- a/samples/net/download/src/main.c +++ b/samples/net/download/src/main.c @@ -11,7 +11,11 @@ #include #include #include -#include +#include + +#include +LOG_MODULE_REGISTER(download, LOG_LEVEL_INF); + #if CONFIG_MODEM_KEY_MGMT #include @@ -49,13 +53,22 @@ static int sec_tag_list[] = { SEC_TAG }; BUILD_ASSERT(sizeof(cert) < KB(4), "Certificate too large"); #endif -static struct download_client downloader; -static struct download_client_cfg config = { +static char dl_buf[2048]; + +static int callback(const struct downloader_evt *event); + +static struct downloader downloader; +static struct downloader_cfg dl_cfg = { + .callback = callback, + .buf = dl_buf, + .buf_size = sizeof(dl_buf), +}; +static struct downloader_host_cfg host_dl_cfg = { #if CONFIG_SAMPLE_SECURE_SOCKET .sec_tag_list = sec_tag_list, .sec_tag_count = ARRAY_SIZE(sec_tag_list), - .set_tls_hostname = true, #endif + .range_override = 0, }; #if CONFIG_SAMPLE_COMPUTE_HASH @@ -162,21 +175,19 @@ static void connectivity_event_handler(struct net_mgmt_event_callback *cb, static void progress_print(size_t downloaded, size_t file_size) { + static int prev_percent; const int percent = (downloaded * 100) / file_size; - size_t lpad = (percent * PROGRESS_WIDTH) / 100; - size_t rpad = PROGRESS_WIDTH - lpad; - printk("\r[ %3d%% ] |", percent); - for (size_t i = 0; i < lpad; i++) { - printk("="); - } - for (size_t i = 0; i < rpad; i++) { - printk(" "); + if (percent == prev_percent) { + return; } - printk("| (%d/%d bytes)", downloaded, file_size); + + prev_percent = percent; + + printk("[ %3d%% ] (%d/%d bytes)\r", percent, downloaded, file_size); } -static int callback(const struct download_client_evt *event) +static int callback(const struct downloader_evt *event) { static size_t downloaded; static size_t file_size; @@ -184,17 +195,17 @@ static int callback(const struct download_client_evt *event) int64_t ms_elapsed; if (downloaded == 0) { - download_client_file_size_get(&downloader, &file_size); + downloader_file_size_get(&downloader, &file_size); downloaded += STARTING_OFFSET; } switch (event->id) { - case DOWNLOAD_CLIENT_EVT_FRAGMENT: + case DOWNLOADER_EVT_FRAGMENT: downloaded += event->fragment.len; if (file_size) { progress_print(downloaded, file_size); } else { - printk("\r[ %d bytes ] ", downloaded); + printk("\r[ %d bytes ]\n", downloaded); } #if CONFIG_SAMPLE_COMPUTE_HASH @@ -203,7 +214,7 @@ static int callback(const struct download_client_evt *event) #endif return 0; - case DOWNLOAD_CLIENT_EVT_DONE: + case DOWNLOADER_EVT_DONE: ms_elapsed = k_uptime_delta(&ref_time); speed = ((float)file_size / ms_elapsed) * MSEC_PER_SEC; printk("\nDownload completed in %lld ms @ %d bytes per sec, total %d bytes\n", @@ -233,7 +244,7 @@ static int callback(const struct download_client_evt *event) printk("Bye\n"); return 0; - case DOWNLOAD_CLIENT_EVT_ERROR: + case DOWNLOADER_EVT_ERROR: printk("Error %d during download\n", event->error); if (event->error == -ECONNRESET) { /* With ECONNRESET, allow library to attempt a reconnect by returning 0 */ @@ -244,8 +255,11 @@ static int callback(const struct download_client_evt *event) return -1; } break; - case DOWNLOAD_CLIENT_EVT_CLOSED: - printk("Socket closed\n"); + case DOWNLOADER_EVT_STOPPED: + printk("Download canceled\n"); + break; + case DOWNLOADER_EVT_DEINITIALIZED: + printk("Client deinitialized\n"); break; } @@ -302,7 +316,7 @@ int main(void) printk("Network connected\n"); - err = download_client_init(&downloader, callback); + err = downloader_init(&downloader, &dl_cfg); if (err) { printk("Failed to initialize the client, err %d", err); return 0; @@ -315,7 +329,7 @@ int main(void) ref_time = k_uptime_get(); - err = download_client_get(&downloader, URL, &config, URL, STARTING_OFFSET); + err = downloader_get(&downloader, &host_dl_cfg, URL, STARTING_OFFSET); if (err) { printk("Failed to start the downloader, err %d", err); return 0; diff --git a/scripts/quarantine_integration.yaml b/scripts/quarantine_integration.yaml index e22bb64aab4b..00b633823706 100644 --- a/scripts/quarantine_integration.yaml +++ b/scripts/quarantine_integration.yaml @@ -1145,8 +1145,8 @@ - scenarios: - sample.net.https_client - sample.net.https_client.lte.tfm-mbedtls - - sample.net.download_client - - sample.net.download_client.ci + - sample.net.downloader + - sample.net.downloader.ci platforms: - nrf9161dk/nrf9161/ns comment: "Configurations excluded to limit resources usage in integration builds" diff --git a/subsys/dfu/dfu_target/Kconfig b/subsys/dfu/dfu_target/Kconfig index cbdee019e5a5..b01f047151f8 100644 --- a/subsys/dfu/dfu_target/Kconfig +++ b/subsys/dfu/dfu_target/Kconfig @@ -68,7 +68,6 @@ config DFU_TARGET_STREAM_SAVE_PROGRESS config DFU_TARGET_MODEM_DELTA bool "Modem delta update support" - imply DOWNLOAD_CLIENT_RANGE_REQUESTS default y depends on SOC_SERIES_NRF91X help diff --git a/subsys/net/lib/CMakeLists.txt b/subsys/net/lib/CMakeLists.txt index 7f91ddd25d50..8dfefee5e712 100644 --- a/subsys/net/lib/CMakeLists.txt +++ b/subsys/net/lib/CMakeLists.txt @@ -16,13 +16,14 @@ if (DEFINED CONFIG_NRF_CLOUD_MQTT OR endif() add_subdirectory_ifdef(CONFIG_REST_CLIENT rest_client) -add_subdirectory_ifdef(CONFIG_DOWNLOAD_CLIENT download_client) -add_subdirectory_ifdef(CONFIG_FOTA_DOWNLOAD fota_download) add_subdirectory_ifdef(CONFIG_AWS_JOBS aws_jobs) add_subdirectory_ifdef(CONFIG_AWS_FOTA aws_fota) add_subdirectory_ifdef(CONFIG_AWS_IOT aws_iot) add_subdirectory_ifdef(CONFIG_AZURE_FOTA azure_fota) add_subdirectory_ifdef(CONFIG_AZURE_IOT_HUB azure_iot_hub) +add_subdirectory_ifdef(CONFIG_DOWNLOAD_CLIENT download_client) +add_subdirectory_ifdef(CONFIG_DOWNLOADER downloader) +add_subdirectory_ifdef(CONFIG_FOTA_DOWNLOAD fota_download) add_subdirectory_ifdef(CONFIG_ZZHC zzhc) add_subdirectory_ifdef(CONFIG_ICAL_PARSER icalendar_parser) add_subdirectory_ifdef(CONFIG_FTP_CLIENT ftp_client) diff --git a/subsys/net/lib/Kconfig b/subsys/net/lib/Kconfig index fe65e6400022..1f8ea1aba3e7 100644 --- a/subsys/net/lib/Kconfig +++ b/subsys/net/lib/Kconfig @@ -29,6 +29,7 @@ endchoice rsource "nrf_cloud/Kconfig" rsource "rest_client/Kconfig" rsource "download_client/Kconfig" +rsource "downloader/Kconfig" rsource "fota_download/Kconfig" rsource "aws_iot/Kconfig" rsource "aws_jobs/Kconfig" diff --git a/subsys/net/lib/aws_fota/src/aws_fota.c b/subsys/net/lib/aws_fota/src/aws_fota.c index 8610724bdb78..c4229fcd229d 100644 --- a/subsys/net/lib/aws_fota/src/aws_fota.c +++ b/subsys/net/lib/aws_fota/src/aws_fota.c @@ -61,8 +61,8 @@ static uint8_t get_topic[AWS_JOBS_TOPIC_MAX_LEN]; /* Allocated buffers for keeping hostname, json payload and file_path. */ static uint8_t payload_buf[CONFIG_AWS_FOTA_PAYLOAD_SIZE]; static uint8_t protocol[sizeof("https://")]; -static uint8_t hostname[CONFIG_DOWNLOAD_CLIENT_MAX_HOSTNAME_SIZE]; -static uint8_t file_path[CONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE]; +static uint8_t hostname[CONFIG_DOWNLOADER_MAX_HOSTNAME_SIZE]; +static uint8_t file_path[CONFIG_DOWNLOADER_MAX_FILENAME_SIZE]; /* Allocated buffer used to keep track the job ID currently being handled by the library. */ static uint8_t job_id_handling[AWS_JOBS_JOB_ID_MAX_LEN] = AWS_JOB_ID_DEFAULT; diff --git a/subsys/net/lib/azure_fota/azure_fota.c b/subsys/net/lib/azure_fota/azure_fota.c index fb31921e3881..7afc9af501d3 100644 --- a/subsys/net/lib/azure_fota/azure_fota.c +++ b/subsys/net/lib/azure_fota/azure_fota.c @@ -72,8 +72,8 @@ static enum fota_status { } current_status = REP_STATUS_CURRENT; static struct fota_object { - char host[CONFIG_DOWNLOAD_CLIENT_MAX_HOSTNAME_SIZE]; - char path[CONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE]; + char host[CONFIG_DOWNLOADER_MAX_HOSTNAME_SIZE]; + char path[CONFIG_DOWNLOADER_MAX_FILENAME_SIZE]; char version[CONFIG_AZURE_FOTA_VERSION_MAX_LEN]; char job_id[CONFIG_AZURE_FOTA_JOB_ID_MAX_LEN]; size_t fragment_size; diff --git a/subsys/net/lib/download_client/Kconfig b/subsys/net/lib/download_client/Kconfig index cde10fd6a288..6f3bb8328730 100644 --- a/subsys/net/lib/download_client/Kconfig +++ b/subsys/net/lib/download_client/Kconfig @@ -5,7 +5,8 @@ # menuconfig DOWNLOAD_CLIENT - bool "Download client" + bool "[DEPRECATED] Download client" + select DEPRECATED if DOWNLOAD_CLIENT diff --git a/subsys/net/lib/download_client/src/download_client.c b/subsys/net/lib/download_client/src/download_client.c index 2077ddeedb09..ef75b34def21 100644 --- a/subsys/net/lib/download_client/src/download_client.c +++ b/subsys/net/lib/download_client/src/download_client.c @@ -731,7 +731,7 @@ static int handle_received(struct download_client *dl, ssize_t len) if (fragment_evt_send(dl)) { /* Restart and suspend */ LOG_INF("Fragment refused, download stopped."); - rc = -1; + return -1; } if (dl->progress == dl->file_size) { diff --git a/subsys/net/lib/downloader/CMakeLists.txt b/subsys/net/lib/downloader/CMakeLists.txt new file mode 100644 index 000000000000..28574a36d14e --- /dev/null +++ b/subsys/net/lib/downloader/CMakeLists.txt @@ -0,0 +1,30 @@ +# +# Copyright (c) 2024 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# +zephyr_library() +zephyr_library_sources( + src/dl_parse.c + src/dl_socket.c + src/downloader.c + src/sanity.c +) + +zephyr_library_sources_ifdef( + CONFIG_DOWNLOADER_TRANSPORT_HTTP + src/transports/http.c +) + +zephyr_library_sources_ifdef( + CONFIG_DOWNLOADER_TRANSPORT_COAP + src/transports/coap.c +) + +zephyr_library_sources_ifdef( + CONFIG_DOWNLOADER_SHELL + src/shell.c +) + +zephyr_include_directories(./include) +zephyr_linker_sources(RODATA dl_transports.ld) diff --git a/subsys/net/lib/downloader/Kconfig b/subsys/net/lib/downloader/Kconfig new file mode 100644 index 000000000000..83624a6571a1 --- /dev/null +++ b/subsys/net/lib/downloader/Kconfig @@ -0,0 +1,67 @@ +# +# Copyright (c) 2024 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +menuconfig DOWNLOADER + bool "Download client" + +if DOWNLOADER + +comment "Thread and stack buffers" + +config DOWNLOADER_STACK_SIZE + int "Thread stack size" + range 768 4096 + default 1280 + +config DOWNLOADER_MAX_HOSTNAME_SIZE + int "Maximum hostname length (stack)" + range 8 256 + default 256 + +config DOWNLOADER_MAX_FILENAME_SIZE + int "Maximum filename length (stack)" + range 8 2048 + default 255 + +config DOWNLOADER_SHELL + bool "Download client shell" + depends on SHELL + +config DOWNLOADER_TRANSPORT_PARAMS_SIZE + int "Maximum transport parameter size" + default 128 + +config DOWNLOADER_TRANSPORT_HTTP + bool "HTTP transport" + depends on NET_IPV4 || NET_IPV6 + default y + +config DOWNLOADER_TRANSPORT_COAP + bool "CoAP transport" + depends on COAP + depends on NET_IPV4 ||NET_IPV6 + +if DOWNLOADER_SHELL + +config DOWNLOADER_SHELL_BUF_SIZE + int "Shell buffer size" + default 2048 + +endif # DOWNLOADER_SHELL + +module=DOWNLOADER +module-dep=LOG +module-str=Download client +source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config" + +if DOWNLOADER_LOG_LEVEL_DBG + +config DOWNLOADER_LOG_HEADERS + bool "Log protocol headers to console [Debug]" + +endif # DOWNLOADER_LOG_LEVEL_DBG + +endif # DOWNLOADER diff --git a/subsys/net/lib/downloader/dl_transports.ld b/subsys/net/lib/downloader/dl_transports.ld new file mode 100644 index 000000000000..ed41ad57b359 --- /dev/null +++ b/subsys/net/lib/downloader/dl_transports.ld @@ -0,0 +1,5 @@ +/* DL transports */ +. = ALIGN(4); +_dl_transport_entry_list_start = .; +KEEP(*(SORT_BY_NAME("._dl_transport_entry.*"))); +_dl_transport_entry_list_end = .; diff --git a/subsys/net/lib/downloader/include/dl_parse.h b/subsys/net/lib/downloader/include/dl_parse.h new file mode 100644 index 000000000000..83bb092e1b1b --- /dev/null +++ b/subsys/net/lib/downloader/include/dl_parse.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef DL_PARSE_H +#define DL_PARSE_H + +#include + +int dl_parse_url_port(const char *url, uint16_t *port); +int dl_parse_url_host(const char *url, char *host, size_t len); +int dl_parse_url_file(const char *url, char *file, size_t len); + +#endif /* DL_PARSE_H */ diff --git a/subsys/net/lib/downloader/include/dl_socket.h b/subsys/net/lib/downloader/include/dl_socket.h new file mode 100644 index 000000000000..1cadb827dd06 --- /dev/null +++ b/subsys/net/lib/downloader/include/dl_socket.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef DL_SOCKET_H +#define DL_SOCKET_H + +#include +#include + +int dl_socket_configure_and_connect( + int *fd, int proto, int type, uint16_t port, struct sockaddr *remote_addr, + const char *hostname, struct downloader_host_cfg *dl_host_cfg); +int dl_socket_close(int *fd); +int dl_socket_send(int fd, void *buf, size_t len); +ssize_t dl_socket_recv(int fd, void *buf, size_t len); +int dl_socket_recv_timeout_set(int fd, uint32_t timeout_ms); +int dl_socket_send_timeout_set(int fd, uint32_t timeout_ms); + +#endif /* DL_SOCKET_H */ diff --git a/subsys/net/lib/downloader/src/dl_parse.c b/subsys/net/lib/downloader/src/dl_parse.c new file mode 100644 index 000000000000..a690abe617ae --- /dev/null +++ b/subsys/net/lib/downloader/src/dl_parse.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include +#include + +#include "dl_parse.h" + +/* + * Trim the string to point to the first character after the substring. + * Returns 0 on success. + */ +static int trim_to_after(const char **str, const char *substr) +{ + const char *p; + + p = strstr(*str, substr); + if (!p) { + return -EINVAL; + } + + *str = p + strlen(substr); + return 0; +} + +int dl_parse_url_host(const char *url, char *host, size_t len) +{ + const char *cur; + const char *end; + + cur = url; + + (void)trim_to_after(&cur, "://"); + + if (cur[0] == '[') { + /* literal IPv6 address */ + end = strchr(cur, ']'); + + if (!end) { + return -EINVAL; + } + ++end; + } else { + end = strchr(cur, ':'); + if (!end) { + end = strchr(cur, '/'); + if (!end) { + end = url + strlen(url) + 1; + } + } + } + + if (end - cur + 1 > len) { + return -E2BIG; + } + + len = end - cur; + + memcpy(host, cur, len); + host[len] = '\0'; + + return 0; +} + +int dl_parse_url_port(const char *url, uint16_t *port) +{ + int err; + const char *cur; + const char *end; + char aport[8]; + size_t len; + + cur = url; + + (void)trim_to_after(&cur, "://"); + + if (cur[0] == '[') { + /* literal IPv6 address */ + (void)trim_to_after(&cur, "]"); + } + + err = trim_to_after(&cur, ":"); + if (err) { + return -EINVAL; + } + + end = strchr(cur, '/'); + if (!end) { + len = strlen(cur); + } else { + len = end - cur; + } + + len = MIN(len, sizeof(aport) - 1); + + memcpy(aport, cur, len); + aport[len] = '\0'; + + *port = atoi(aport); + + return 0; +} + +int dl_parse_url_file(const char *url, char *file, size_t len) +{ + int err; + const char *cur; + + cur = url; + + if (strstr(url, "//")) { + err = trim_to_after(&cur, "://"); + if (err) { + return -EINVAL; + } + } + + err = trim_to_after(&cur, "/"); + if (err) { + return -EINVAL; + } + + if (strlen(cur) + 1 > len) { + return -E2BIG; + } + + len = strlen(cur); + + memcpy(file, cur, len); + file[len] = '\0'; + + return 0; +} diff --git a/subsys/net/lib/downloader/src/dl_socket.c b/subsys/net/lib/downloader/src/dl_socket.c new file mode 100644 index 000000000000..92c648f0bbe1 --- /dev/null +++ b/subsys/net/lib/downloader/src/dl_socket.c @@ -0,0 +1,398 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#if defined(CONFIG_POSIX_API) +#include +#else +#include +#endif +#include +#include +#include +#include + +#include "dl_socket.h" + +#include +LOG_MODULE_DECLARE(downloader, CONFIG_DOWNLOADER_LOG_LEVEL); + +#define SIN6(A) ((struct sockaddr_in6 *)(A)) +#define SIN(A) ((struct sockaddr_in *)(A)) + +#define MS_TO_TIMEVAL(ms) \ + .tv_sec = (ms / 1000), \ + .tv_usec = (ms % 1000) * 1000 \ + +static const char *str_family(int family) +{ + switch (family) { + case AF_UNSPEC: + return "Unspec"; + case AF_INET: + return "IPv4"; + case AF_INET6: + return "IPv6"; + default: + __ASSERT(false, "Unsupported family"); + return "Unknown"; + } +} + +static int socket_sectag_set(int fd, const int *const sec_tag_list, uint8_t sec_tag_count) +{ + int err; + int verify; + + verify = TLS_PEER_VERIFY_REQUIRED; + + err = setsockopt(fd, SOL_TLS, TLS_PEER_VERIFY, &verify, sizeof(verify)); + if (err) { + LOG_ERR("Failed to setup peer verification, errno %d", errno); + return -errno; + } + + LOG_INF("Setting up TLS credentials, sec tag count %u", sec_tag_count); + err = setsockopt(fd, SOL_TLS, TLS_SEC_TAG_LIST, sec_tag_list, + sizeof(sec_tag_t) * sec_tag_count); + if (err) { + LOG_ERR("Failed to setup socket security tag list, errno %d", errno); + return -errno; + } + + return 0; +} + +static int socket_tls_hostname_set(int fd, const char *const hostname) +{ + __ASSERT_NO_MSG(hostname); + + int err; + + err = setsockopt(fd, SOL_TLS, TLS_HOSTNAME, hostname, strlen(hostname)); + if (err) { + LOG_ERR("Failed to setup TLS hostname (%s), errno %d", hostname, errno); + return -errno; + } + + return 0; +} + +static int socket_pdn_id_set(int fd, uint32_t pdn_id) +{ + int err; + + LOG_INF("Binding to PDN ID: %d", pdn_id); + err = setsockopt(fd, SOL_SOCKET, SO_BINDTOPDN, &pdn_id, sizeof(pdn_id)); + if (err) { + LOG_ERR("Failed to bind socket to PDN ID %d, err %d", pdn_id, errno); + return -ENETDOWN; + } + + return 0; +} + +static int socket_dtls_cid_enable(int fd) +{ + int err; + uint32_t dtls_cid = TLS_DTLS_CID_ENABLED; + + err = setsockopt(fd, SOL_TLS, TLS_DTLS_CID, &dtls_cid, sizeof(dtls_cid)); + if (err) { + err = -errno; + LOG_ERR("Failed to enable TLS_DTLS_CID: %d", err); + /* Not fatal, so continue */ + } + + return err; +} + +static bool is_ip_address(const char *hostname) +{ + struct sockaddr sa; + + if (zsock_inet_pton(AF_INET, hostname, sa.data) == 1) { + return true; + } else if (zsock_inet_pton(AF_INET6, hostname, sa.data) == 1) { + return true; + } + + return false; +} + +static int dl_socket_host_lookup(const char *const hostname, uint32_t pdn_id, struct sockaddr *sa, + int family) +{ + int err; + char pdnserv[4]; + char *servname = NULL; + struct addrinfo *ai; + struct addrinfo hints = { + .ai_family = family, + }; + +#if !defined(CONFIG_NET_IPV6) + if (family == AF_INET6) { + return -EINVAL; + } +#endif + + LOG_DBG("host lookup %s, pdn id %d, family %d", hostname, pdn_id, family); + + if (pdn_id) { + hints.ai_flags = AI_PDNSERV; + (void)snprintf(pdnserv, sizeof(pdnserv), "%d", pdn_id); + servname = pdnserv; + } + + err = getaddrinfo(hostname, servname, &hints, &ai); + if (err) { + /* We expect this to fail on IPv6 sometimes */ + LOG_INF("Failed to resolve hostname %s on %s, err %d", hostname, + str_family(hints.ai_family), err); + return -EHOSTUNREACH; + } + + memcpy(sa, ai->ai_addr, ai->ai_addrlen); + freeaddrinfo(ai); + + return 0; +} + +static int dl_socket_create_and_connect(int *fd, int proto, int type, uint16_t port, + struct sockaddr *remote_addr, const char *hostname, + struct downloader_host_cfg *dl_host_cfg) +{ + int err; + socklen_t addrlen; + + switch (remote_addr->sa_family) { + case AF_INET6: + SIN6(remote_addr)->sin6_port = htons(port); + addrlen = sizeof(struct sockaddr_in6); + break; + case AF_INET: + SIN(remote_addr)->sin_port = htons(port); + addrlen = sizeof(struct sockaddr_in); + break; + default: + err = -EAFNOSUPPORT; + goto cleanup; + } + + LOG_DBG("family: %d, type: %d, proto: %d", remote_addr->sa_family, type, proto); + + *fd = socket(remote_addr->sa_family, type, proto); + if (*fd < 0) { + err = -errno; + LOG_ERR("Failed to create socket, errno %d", -err); + goto cleanup; + } + + LOG_DBG("Socket opened, fd %d", *fd); + + if (dl_host_cfg->pdn_id) { + err = socket_pdn_id_set(*fd, dl_host_cfg->pdn_id); + if (err) { + goto cleanup; + } + } + + if ((proto == IPPROTO_TLS_1_2 || proto == IPPROTO_DTLS_1_2) && + (dl_host_cfg->sec_tag_list != NULL) && (dl_host_cfg->sec_tag_count > 0)) { + err = socket_sectag_set(*fd, dl_host_cfg->sec_tag_list, dl_host_cfg->sec_tag_count); + if (err) { + goto cleanup; + } + + if (proto == IPPROTO_TLS_1_2 && !is_ip_address(hostname)) { + err = socket_tls_hostname_set(*fd, hostname); + if (err) { + goto cleanup; + } + } + + if (proto == IPPROTO_DTLS_1_2 && dl_host_cfg->cid) { + LOG_DBG("enabling CID"); + err = socket_dtls_cid_enable(*fd); + if (err) { + goto cleanup; + } + } + } + + if (IS_ENABLED(CONFIG_LOG)) { + char ip_addr_str[NET_IPV6_ADDR_LEN]; + void *sin_addr; + + if (remote_addr->sa_family == AF_INET6) { + sin_addr = &((struct sockaddr_in6 *)remote_addr)->sin6_addr; + } else { + sin_addr = &((struct sockaddr_in *)remote_addr)->sin_addr; + } + inet_ntop(remote_addr->sa_family, sin_addr, ip_addr_str, sizeof(ip_addr_str)); + LOG_INF("Connecting to %s", ip_addr_str); + } + LOG_DBG("fd %d, addrlen %d, fam %s, port %d", *fd, addrlen, + str_family(remote_addr->sa_family), port); + + err = connect(*fd, remote_addr, addrlen); + if (err) { + err = -errno; + /* Make sure that ECONNRESET is not returned as it has a special meaning + * in the downloader API + */ + if (err == -ECONNRESET) { + err = -ECONNREFUSED; + } + } + +cleanup: + if (err) { + dl_socket_close(fd); + } + + return err; +} + +int dl_socket_configure_and_connect(int *fd, int proto, int type, uint16_t port, + struct sockaddr *remote_addr, const char *hostname, + struct downloader_host_cfg *dl_host_cfg) +{ + int err = -1; + int fam; + + if (remote_addr->sa_family) { + goto connect; + } + + fam = dl_host_cfg->family ? dl_host_cfg->family : AF_INET6; + + err = dl_socket_host_lookup(hostname, dl_host_cfg->pdn_id, remote_addr, fam); + if (!err) { + goto connect; + } else if (dl_host_cfg->family) { + LOG_ERR("Host lookup failed for hostname %s, err %d", hostname, err); + return err; + } + + LOG_INF("Host lookup failed for hostname %s on IPv6 (err %d), attempting IPv4", + hostname, err); + + +fallback_ipv4: + err = dl_socket_host_lookup(hostname, dl_host_cfg->pdn_id, remote_addr, AF_INET); + if (err) { + LOG_ERR("Host lookup failed for hostname %s, err %d", hostname, err); + return err; + } + +connect: + err = dl_socket_create_and_connect(fd, proto, type, port, remote_addr, hostname, + dl_host_cfg); + if (err) { + if (remote_addr->sa_family == AF_INET6) { + LOG_INF("Failed to connect on IPv6 (err %d), attempting IPv4", err); + goto fallback_ipv4; + } + + LOG_ERR("Failed to connect, err %d", err); + return err; + } + + return 0; +} + +int dl_socket_close(int *fd) +{ + int err = 0; + + if (*fd >= 0) { + err = close(*fd); + if (err && errno != EBADF) { + err = -errno; + LOG_ERR("Failed to close socket, errno %d", -err); + } + + LOG_DBG("Socket closed, fd %d", *fd); + *fd = -1; + } + + return err; +} + +int dl_socket_send(int fd, void *buf, size_t len) +{ + int sent; + size_t off = 0; + + while (len) { + sent = send(fd, (uint8_t *)buf + off, len, 0); + if (sent < 0) { + return -errno; + } + + off += sent; + len -= sent; + } + + return 0; +} + +int dl_socket_send_timeout_set(int fd, uint32_t timeout_ms) +{ + int err; + struct timeval timeo = { + MS_TO_TIMEVAL(timeout_ms), + }; + + if (timeout_ms <= 0) { + return 0; + } + + err = setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo, sizeof(timeo)); + if (err) { + LOG_WRN("Failed to set socket timeout, errno %d", errno); + return -errno; + } + + return 0; +} + +int dl_socket_recv_timeout_set(int fd, uint32_t timeout_ms) +{ + int err; + struct timeval timeo = { + MS_TO_TIMEVAL(timeout_ms), + }; + + if (fd == -1) { + return -EINVAL; + } + + err = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo)); + if (err) { + LOG_WRN("Failed to set socket timeout, errno %d", errno); + return -errno; + } + + return 0; +} + +ssize_t dl_socket_recv(int fd, void *buf, size_t len) +{ + int err = 0; + + if (fd == -1) { + return -EINVAL; + } + + err = recv(fd, buf, len, 0); + if (err < 0) { + return -errno; + } + + return err; +} diff --git a/subsys/net/lib/downloader/src/downloader.c b/subsys/net/lib/downloader/src/downloader.c new file mode 100644 index 000000000000..e2e4acd902ce --- /dev/null +++ b/subsys/net/lib/downloader/src/downloader.c @@ -0,0 +1,628 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "dl_parse.h" +#include "dl_socket.h" + +#include +LOG_MODULE_REGISTER(downloader, CONFIG_DOWNLOADER_LOG_LEVEL); + +static int stopped_evt_send(struct downloader *dl); + +#define FALLBACK_HTTP "http://" +#define FALLBACK_HTTPS "https://" + +#define STATE_ALLOW_ANY 0xffffffff + +#if defined(CONFIG_DOWNLOADER_LOG_LEVEL_WRN) +char *state_to_str(int state) +{ + switch (state) { + case DOWNLOADER_IDLE: + return "IDLE"; + case DOWNLOADER_CONNECTING: + return "CONNECTING"; + case DOWNLOADER_CONNECTED: + return "CONNECTED"; + case DOWNLOADER_DOWNLOADING: + return "DOWNLOADING"; + case DOWNLOADER_STOPPING: + return "STOPPING"; + case DOWNLOADER_DEINITIALIZING: + return "DEINITIALIZING"; + case DOWNLOADER_DEINITIALIZED: + return "DEINITIALIZED"; + } + + return "unknown"; +} +#else +char *state_to_str(int state) +{ + return ""; +} +#endif + +static void state_set(struct downloader *dl, unsigned int before_state, unsigned int new_state) +{ + k_mutex_lock(&dl->mutex, K_FOREVER); + if ((dl->state != before_state) && (before_state != STATE_ALLOW_ANY)) { + k_mutex_unlock(&dl->mutex); + return; + } + + dl->state = new_state; + k_mutex_unlock(&dl->mutex); + LOG_DBG("state = %d (%s)", new_state, state_to_str(new_state)); +} + +static bool is_state(struct downloader *dl, enum downloader_state state) +{ + bool ret; + + k_mutex_lock(&dl->mutex, K_FOREVER); + ret = dl->state == state; + k_mutex_unlock(&dl->mutex); + return ret; +} + +static bool is_valid_state(struct downloader *dl, enum downloader_state state) +{ + k_mutex_lock(&dl->mutex, K_FOREVER); + switch (state) { + case DOWNLOADER_DEINITIALIZED: + case DOWNLOADER_IDLE: + case DOWNLOADER_CONNECTING: + case DOWNLOADER_CONNECTED: + case DOWNLOADER_DOWNLOADING: + case DOWNLOADER_STOPPING: + case DOWNLOADER_DEINITIALIZING: + k_mutex_unlock(&dl->mutex); + return true; + default: + break; + } + + k_mutex_unlock(&dl->mutex); + return false; +} + +static int transport_init(struct downloader *dl, struct downloader_host_cfg *dl_host_cfg, + const char *url) +{ + __ASSERT_NO_MSG(dl && dl->transport); + + return dl->transport->init(dl, dl_host_cfg, url); +} + +static int transport_deinit(struct downloader *dl) +{ + int err; + + __ASSERT_NO_MSG(dl && dl->transport); + + err = dl->transport->deinit(dl); + dl->transport = NULL; + + return err; +} + +static int transport_connect(struct downloader *dl) +{ + __ASSERT_NO_MSG(dl && dl->transport); + + return dl->transport->connect(dl); +} + +static int transport_close(struct downloader *dl) +{ + __ASSERT_NO_MSG(dl && dl->transport); + + return dl->transport->close(dl); +} + +static int transport_download(struct downloader *dl) +{ + __ASSERT_NO_MSG(dl && dl->transport); + + return dl->transport->download(dl); +} + +static int reconnect(struct downloader *dl) +{ + int err = 0; + + LOG_DBG("Reconnecting..."); + + err = transport_close(dl); + if (err) { + LOG_DBG("disconnect failed, %d", err); + } + + return transport_connect(dl); +} + +static void restart_and_suspend(struct downloader *dl) +{ + if (!is_state(dl, DOWNLOADER_DOWNLOADING)) { + return; + } + + if (!dl->host_cfg.keep_connection) { + transport_close(dl); + if (!dl->complete) { + stopped_evt_send(dl); + } + + state_set(dl, DOWNLOADER_DOWNLOADING, DOWNLOADER_IDLE); + return; + } + + if (!dl->complete) { + stopped_evt_send(dl); + } + state_set(dl, DOWNLOADER_DOWNLOADING, DOWNLOADER_CONNECTED); +} + +static int data_evt_send(const struct downloader *dl, void *data, size_t len) +{ + const struct downloader_evt evt = {.id = DOWNLOADER_EVT_FRAGMENT, + .fragment = { + .buf = data, + .len = len, + }}; + + return dl->cfg.callback(&evt); +} + +static int download_complete_evt_send(const struct downloader *dl) +{ + const struct downloader_evt evt = { + .id = DOWNLOADER_EVT_DONE, + }; + + return dl->cfg.callback(&evt); +} + +static int stopped_evt_send(struct downloader *dl) +{ + const struct downloader_evt evt = { + .id = DOWNLOADER_EVT_STOPPED, + }; + + return dl->cfg.callback(&evt); +} + +static int error_evt_send(const struct downloader *dl, int error) +{ + /* Error will be sent as negative. */ + __ASSERT_NO_MSG(error < 0); + + const struct downloader_evt evt = {.id = DOWNLOADER_EVT_ERROR, .error = error}; + + return dl->cfg.callback(&evt); +} + +static int deinit_evt_send(const struct downloader *dl) +{ + const struct downloader_evt evt = { + .id = DOWNLOADER_EVT_DEINITIALIZED, + }; + + return dl->cfg.callback(&evt); +} + +/* Events from the transport */ +int dl_transport_evt_data(struct downloader *dl, void *data, size_t len) +{ + int err; + + LOG_DBG("Read %d bytes from transport", len); + + if (dl->file_size) { + LOG_INF("Downloaded %u/%u bytes (%d%%)", dl->progress, dl->file_size, + (dl->progress * 100) / dl->file_size); + } else { + LOG_INF("Downloaded %u bytes", dl->progress); + } + + err = data_evt_send(dl, data, len); + if (err) { + /* Application refused data, suspend */ + restart_and_suspend(dl); + } + + return 0; +} + +void download_thread(void *cli, void *a, void *b) +{ + int rc, rc2; + struct downloader *const dl = cli; + + while (true) { + rc = 0; + + if (is_state(dl, DOWNLOADER_IDLE)) { + /* Client idle, wait for action */ + k_sem_take(&dl->event_sem, K_FOREVER); + } + + /* Connect to the target host */ + if (is_state(dl, DOWNLOADER_CONNECTING)) { + /* Client */ + rc = transport_connect(dl); + if (rc) { + rc = error_evt_send(dl, rc); + if (rc) { + stopped_evt_send(dl); + state_set(dl, DOWNLOADER_CONNECTING, DOWNLOADER_IDLE); + continue; + } + continue; + } + + /* Connection successful */ + state_set(dl, DOWNLOADER_CONNECTING, DOWNLOADER_DOWNLOADING); + } + + if (is_state(dl, DOWNLOADER_CONNECTED)) { + /* Client connected, wait for action */ + k_sem_take(&dl->event_sem, K_FOREVER); + } + + if (is_state(dl, DOWNLOADER_DOWNLOADING)) { + /* Download until transport returns an error or the download is complete + * (separate event). + */ + rc = transport_download(dl); + if (rc) { + if (rc == -ECONNRESET) { + goto reconnect; + } + + rc = error_evt_send(dl, rc); + if (rc) { + restart_and_suspend(dl); + continue; + } + +reconnect: + rc2 = reconnect(dl); + if (rc2 == 0) { + continue; + } + + LOG_ERR("Failed to reconnect, err %d", rc2); + if (rc == -ECONNRESET) { + /* We haven't sent the error before in this case, + * so we do it now. + */ + rc2 = error_evt_send(dl, rc); + if (rc2 == 0 && is_state(dl, DOWNLOADER_DOWNLOADING)) { + goto reconnect; + } + } + + transport_close(dl); + stopped_evt_send(dl); + state_set(dl, DOWNLOADER_DOWNLOADING, DOWNLOADER_IDLE); + continue; + } + + if (dl->complete) { + LOG_INF("Download complete"); + restart_and_suspend(dl); + download_complete_evt_send(dl); + } + } + + if (is_state(dl, DOWNLOADER_STOPPING)) { + if (!dl->host_cfg.keep_connection) { + transport_close(dl); + state_set(dl, DOWNLOADER_STOPPING, DOWNLOADER_IDLE); + } else { + state_set(dl, DOWNLOADER_STOPPING, DOWNLOADER_CONNECTED); + } + + stopped_evt_send(dl); + } + + if (is_state(dl, DOWNLOADER_DEINITIALIZING)) { + if (dl->transport) { + transport_close(dl); + transport_deinit(dl); + } + + state_set(dl, DOWNLOADER_DEINITIALIZING, DOWNLOADER_DEINITIALIZED); + deinit_evt_send(dl); + return; + } + } +} + +int downloader_init(struct downloader *const dl, struct downloader_cfg *dl_cfg) +{ + if (!dl || !dl_cfg || !dl_cfg->callback || !dl_cfg->buf || dl_cfg->buf_size == 0) { + return -EINVAL; + } + + memset(dl, 0, sizeof(*dl)); + dl->cfg = *dl_cfg; + k_sem_init(&dl->event_sem, 0, 1); + k_mutex_init(&dl->mutex); + + k_mutex_lock(&dl->mutex, K_FOREVER); + + /* The thread is spawned now, but it will suspend itself; + * it is resumed when the download is started via the API. + */ + dl->tid = k_thread_create(&dl->thread, dl->thread_stack, + K_THREAD_STACK_SIZEOF(dl->thread_stack), download_thread, dl, + NULL, NULL, K_LOWEST_APPLICATION_THREAD_PRIO, 0, K_NO_WAIT); + + k_thread_name_set(dl->tid, "downloader"); + + dl->state = DOWNLOADER_IDLE; + + k_mutex_unlock(&dl->mutex); + + return 0; +} + +int downloader_deinit(struct downloader *const dl) +{ + if (!dl) { + return -EINVAL; + } + + if (!is_valid_state(dl, dl->state) || is_state(dl, DOWNLOADER_DEINITIALIZED)) { + return -EPERM; + } + + if (is_state(dl, DOWNLOADER_CONNECTING) || + (is_state(dl, DOWNLOADER_DOWNLOADING) && (dl->file_size != dl->progress))) { + error_evt_send(dl, -ECANCELED); + stopped_evt_send(dl); + } + + state_set(dl, STATE_ALLOW_ANY, DOWNLOADER_DEINITIALIZING); + k_sem_give(&dl->event_sem); + + k_thread_join(&dl->thread, K_FOREVER); + + return 0; +} + +static int downloader_start(struct downloader *dl, const struct downloader_host_cfg *dl_host_cfg, + const char *url, size_t from) +{ + __ASSERT_NO_MSG(dl != NULL); + __ASSERT_NO_MSG(dl_host_cfg != NULL); + __ASSERT_NO_MSG(url != NULL); + + int err; + const struct dl_transport *transport_connected = dl->transport; + bool host_connected = false; + + LOG_DBG("URL: %s", url); + + k_mutex_lock(&dl->mutex, K_FOREVER); + + if (!is_state(dl, DOWNLOADER_IDLE) && !is_state(dl, DOWNLOADER_CONNECTED)) { + LOG_ERR("Invalid start state: %d", dl->state); + k_mutex_unlock(&dl->mutex); + return -EPERM; + } + + /* Check if we are already connected to the correct host */ + if (is_state(dl, DOWNLOADER_CONNECTED)) { + char hostname[CONFIG_DOWNLOADER_MAX_HOSTNAME_SIZE]; + + err = dl_parse_url_host(url, hostname, sizeof(hostname)); + if (err) { + LOG_ERR("Failed to parse hostname"); + k_mutex_unlock(&dl->mutex); + return -EINVAL; + } + if (strncmp(hostname, dl->hostname, sizeof(hostname)) == 0) { + host_connected = true; + } + } + + /* Extract the hostname, without protocol or port */ + err = dl_parse_url_host(url, dl->hostname, sizeof(dl->hostname)); + if (err) { + LOG_ERR("Failed to parse hostname, err %d", err); + k_mutex_unlock(&dl->mutex); + return -EINVAL; + } + + /* Extract the filename, without protocol or port */ + err = dl_parse_url_file(url, dl->file, sizeof(dl->file)); + if (err) { + LOG_ERR("Failed to parse filename, err %d, url %s", err, url); + k_mutex_unlock(&dl->mutex); + return err; + } + + dl->host_cfg = *dl_host_cfg; + dl->file_size = 0; + dl->progress = from; + dl->buf_offset = 0; + dl->complete = false; + + dl->transport = NULL; + STRUCT_SECTION_FOREACH(dl_transport_entry, entry) + { + if (entry->transport->proto_supported(dl, url)) { + dl->transport = entry->transport; + break; + } + } + + if (!dl->transport) { + if (strstr(url, "://") == NULL) { + char *fallback = FALLBACK_HTTP; + + if (dl_host_cfg->sec_tag_list && dl_host_cfg->sec_tag_count) { + fallback = FALLBACK_HTTPS; + } + + LOG_WRN("Protocol not specified for %s, attempting %s", url, fallback); + STRUCT_SECTION_FOREACH(dl_transport_entry, entry) + { + if (entry->transport->proto_supported(dl, fallback)) { + dl->transport = entry->transport; + break; + } + } + } + + if (!dl->transport) { + LOG_ERR("Protocol not found for %s", url); + k_mutex_unlock(&dl->mutex); + return -EPROTONOSUPPORT; + } + }; + + if (is_state(dl, DOWNLOADER_CONNECTED)) { + if (host_connected) { + state_set(dl, DOWNLOADER_CONNECTED, DOWNLOADER_DOWNLOADING); + goto out; + } else if (transport_connected) { + /* We are connected to the wrong host */ + LOG_DBG("Closing connection to connect different host or protocol"); + transport_connected->close(dl); + transport_connected->deinit(dl); + state_set(dl, DOWNLOADER_CONNECTED, DOWNLOADER_IDLE); + } + } + + state_set(dl, DOWNLOADER_IDLE, DOWNLOADER_CONNECTING); + + err = transport_init(dl, &dl->host_cfg, url); + if (err) { + state_set(dl, DOWNLOADER_CONNECTING, DOWNLOADER_IDLE); + k_mutex_unlock(&dl->mutex); + LOG_ERR("Failed to initialize transport, err %d", err); + return err; + } + +out: + k_mutex_unlock(&dl->mutex); + + /* Let the thread run */ + k_sem_give(&dl->event_sem); + return 0; +} + +int downloader_cancel(struct downloader *const dl) +{ + if (!dl) { + return -EINVAL; + } + + k_mutex_lock(&dl->mutex, K_FOREVER); + if (!is_state(dl, DOWNLOADER_CONNECTING) && !is_state(dl, DOWNLOADER_DOWNLOADING)) { + k_mutex_unlock(&dl->mutex); + return -EPERM; + } + + state_set(dl, STATE_ALLOW_ANY, DOWNLOADER_STOPPING); + k_mutex_unlock(&dl->mutex); + return 0; +} + +int downloader_get(struct downloader *dl, const struct downloader_host_cfg *dl_host_cfg, + const char *url, size_t from) +{ + int rc; + + if (!dl || !dl_host_cfg || !url) { + return -EINVAL; + } + + rc = downloader_start(dl, dl_host_cfg, url, from); + + return rc; +} + +int downloader_get_with_host_and_file(struct downloader *dl, + const struct downloader_host_cfg *dl_host_cfg, + const char *host, const char *file, size_t from) +{ + int rc; + + if (!dl || !dl_host_cfg || !host || !file) { + return -EINVAL; + } + + k_mutex_lock(&dl->mutex, K_FOREVER); + + if (!is_state(dl, DOWNLOADER_IDLE) && !is_state(dl, DOWNLOADER_CONNECTED)) { + LOG_ERR("Invalid start state: %d", dl->state); + k_mutex_unlock(&dl->mutex); + return -EPERM; + } + + /* We use the download client buffer to parse the url */ + if (strlen(host) + strlen(file) + 2 > dl->cfg.buf_size) { + LOG_ERR("Download client buffer is not large enough to parse url"); + k_mutex_unlock(&dl->mutex); + return -EINVAL; + } + + snprintf(dl->cfg.buf, dl->cfg.buf_size, "%s/%s", host, file); + + rc = downloader_start(dl, dl_host_cfg, dl->cfg.buf, from); + + k_mutex_unlock(&dl->mutex); + + return rc; +} + +int downloader_file_size_get(struct downloader *dl, size_t *size) +{ + if (!dl || !size) { + return -EINVAL; + } + + if (is_state(dl, DOWNLOADER_DEINITIALIZED)) { + return -EPERM; + } + + k_mutex_lock(&dl->mutex, K_FOREVER); + *size = dl->file_size; + k_mutex_unlock(&dl->mutex); + + return 0; +} + +int downloader_downloaded_size_get(struct downloader *dl, size_t *size) +{ + if (!dl || !size) { + return -EINVAL; + } + + if (is_state(dl, DOWNLOADER_DEINITIALIZED)) { + return -EPERM; + } + + k_mutex_lock(&dl->mutex, K_FOREVER); + *size = dl->progress; + k_mutex_unlock(&dl->mutex); + + return 0; +} diff --git a/subsys/net/lib/downloader/src/sanity.c b/subsys/net/lib/downloader/src/sanity.c new file mode 100644 index 000000000000..1033d78005b5 --- /dev/null +++ b/subsys/net/lib/downloader/src/sanity.c @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include + +#define HOSTNAME_SIZE CONFIG_DOWNLOADER_MAX_HOSTNAME_SIZE +#define STACK_SIZE CONFIG_DOWNLOADER_STACK_SIZE + +/* Ensure that the stack size is large enough + * to accommodate for host and file names + */ + +BUILD_ASSERT(STACK_SIZE - HOSTNAME_SIZE >= 512, "Your stack size is too small"); diff --git a/subsys/net/lib/downloader/src/shell.c b/subsys/net/lib/downloader/src/shell.c new file mode 100644 index 000000000000..36ec5711f032 --- /dev/null +++ b/subsys/net/lib/downloader/src/shell.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include +#include +#include + +LOG_MODULE_DECLARE(downloader); + +static int dl_callback(const struct downloader_evt *event); + +/** Downloader buffer */ +static char dl_buf[CONFIG_DOWNLOADER_SHELL_BUF_SIZE]; +static struct downloader downloader; +static struct downloader_cfg dl_cfg = { + .callback = dl_callback, + .buf = dl_buf, + .buf_size = sizeof(dl_buf), +}; + +static int sec_tag_list[1]; +static struct downloader_host_cfg dl_host_cfg = { + .sec_tag_list = sec_tag_list, +}; + +static char url[CONFIG_DOWNLOADER_MAX_HOSTNAME_SIZE + CONFIG_DOWNLOADER_MAX_FILENAME_SIZE + 1]; +static bool in_progress; + +static const struct shell *shell_instance; + +static int dl_callback(const struct downloader_evt *event) +{ + static size_t downloaded; + static size_t file_size; + + if (downloaded == 0) { + downloader_file_size_get(&downloader, &file_size); + } + + switch (event->id) { + case DOWNLOADER_EVT_FRAGMENT: + downloaded += event->fragment.len; + if (file_size) { + shell_fprintf(shell_instance, SHELL_NORMAL, "\r[ %d%% ] ", + (downloaded * 100) / file_size); + } else { + shell_fprintf(shell_instance, SHELL_NORMAL, "\r[ %d bytes ] ", downloaded); + } + break; + case DOWNLOADER_EVT_DONE: + shell_print(shell_instance, "done (%d bytes)", downloaded); + downloaded = 0; + break; + case DOWNLOADER_EVT_ERROR: + shell_error(shell_instance, "error %d during download", event->error); + downloaded = 0; + in_progress = false; + break; + case DOWNLOADER_EVT_STOPPED: + shell_print(shell_instance, "download client closed"); + in_progress = false; + case DOWNLOADER_EVT_DEINITIALIZED: + shell_print(shell_instance, "client deinitialized"); + in_progress = false; + break; + } + + return 0; +} + +static int cmd_dc_init(const struct shell *shell, size_t argc, char **argv) +{ + int err; + + shell_instance = shell; + + err = downloader_init(&downloader, &dl_cfg); + if (err) { + shell_warn(shell, "Download client init failed %d\n", err); + } + + shell_print(shell, "Download client initialized\n"); + + return err; +} + +static int cmd_dc_deinit(const struct shell *shell, size_t argc, char **argv) +{ + int err; + + err = downloader_deinit(&downloader); + if (err) { + shell_warn(shell, "Download client deinit failed %d\n", err); + } + + return err; +} + +static int cmd_host_config_pdn_id(const struct shell *shell, size_t argc, char **argv) +{ + if (argc != 2) { + shell_warn(shell, "usage: dc host_config pdn \n"); + return -EINVAL; + } + + dl_host_cfg.pdn_id = atoi(argv[1]); + + shell_print(shell, "PDN ID set: %d\n", dl_host_cfg.pdn_id); + return 0; +} + +static int cmd_host_config_sec_tag(const struct shell *shell, size_t argc, char **argv) +{ + if (argc != 2) { + shell_warn(shell, "usage: dc host_config sec_tag \n"); + return -EINVAL; + } + + sec_tag_list[0] = atoi(argv[1]); + dl_host_cfg.sec_tag_count = 1; + + shell_print(shell, "Security tag set: %d\n", dl_host_cfg.sec_tag_list[0]); + return 0; +} + +static int cmd_host_config_native_tls(const struct shell *shell, size_t argc, char **argv) +{ + if (argc != 2) { + shell_warn(shell, "usage: dc host_config native_tls <0/1>\n"); + return -EINVAL; + } + + dl_host_cfg.set_native_tls = atoi(argv[1]); + + shell_print(shell, "Native tls %s\n", dl_host_cfg.set_native_tls ? "enabled" : "disabled"); + + return 0; +} + +static int cmd_host_config_keep_connection(const struct shell *shell, size_t argc, char **argv) +{ + if (argc != 2) { + shell_warn(shell, "usage: dc host_config keep_connection <0/1>\n"); + return -EINVAL; + } + + dl_host_cfg.keep_connection = atoi(argv[1]); + + shell_print(shell, "Keep connection %s\n", + dl_host_cfg.keep_connection ? "enabled" : "disabled"); + + return 0; +} + +static int cmd_download_get(const struct shell *shell, size_t argc, char **argv) +{ + int err; + size_t from = 0; + + shell_instance = shell; + + if (argc < 2 || argc > 3) { + shell_warn(shell, "usage: dc get [offset]"); + return -EINVAL; + } + + if (argc == 3) { + from = atoi(argv[2]); + } + + if (in_progress) { + return -EALREADY; + } + + strncpy(url, argv[1], sizeof(url)); + url[sizeof(url) - 1] = '\0'; + + err = downloader_get(&downloader, &dl_host_cfg, url, from); + + if (err) { + shell_warn(shell, "downloader_get() failed, err %d", err); + return -ENOEXEC; + } + + in_progress = true; + shell_print(shell, "Downloading"); + + return 0; +} + +static int cmd_download_cancel(const struct shell *shell, size_t argc, char **argv) +{ + int err; + + err = downloader_cancel(&downloader); + if (err) { + shell_warn(shell, "downloader_cancel() failed, err %d", err); + } else { + shell_print(shell, "Download cancelled"); + } + return 0; +} + +static int cmd_download_file_size_get(const struct shell *shell, size_t argc, char **argv) +{ + int err; + size_t fs; + + err = downloader_file_size_get(&downloader, &fs); + if (err) { + shell_warn(shell, "downloader_file_size_get() failed, err %d", err); + } else { + shell_print(shell, "File size: %d", fs); + } + return 0; +} + +static int cmd_download_progress_get(const struct shell *shell, size_t argc, char **argv) +{ + int err; + size_t fs; + + err = downloader_downloaded_size_get(&downloader, &fs); + if (err) { + shell_warn(shell, "downloader_downloaded_size_get() failed, err %d", err); + } else { + shell_print(shell, "Downloaded: %d", fs); + } + return 0; +} + +SHELL_STATIC_SUBCMD_SET_CREATE( + host_config_options, + SHELL_CMD(sec_tag, NULL, "Set security tag", cmd_host_config_sec_tag), + SHELL_CMD(pdn_id, NULL, "Set PDN ID", cmd_host_config_pdn_id), + SHELL_CMD(native_tls, NULL, "Enable native TLS", cmd_host_config_native_tls), + SHELL_CMD(keep_connection, NULL, "Keep host connection", cmd_host_config_keep_connection), + SHELL_SUBCMD_SET_END); + +SHELL_STATIC_SUBCMD_SET_CREATE(download_options, SHELL_CMD(get, NULL, "Get file", cmd_download_get), + SHELL_CMD(cancel, NULL, "Cancel download", cmd_download_cancel), + SHELL_CMD(file_size, NULL, "Get file size", + cmd_download_file_size_get), + SHELL_CMD(progress, NULL, "Get bytes downloaded", + cmd_download_progress_get), + SHELL_SUBCMD_SET_END); + +SHELL_STATIC_SUBCMD_SET_CREATE( + sub_dl, SHELL_CMD(init, NULL, "Initialize download client", cmd_dc_init), + SHELL_CMD(deinit, NULL, "Deinitialize download client", cmd_dc_deinit), + SHELL_CMD(host_config, &host_config_options, "Set configuration option", NULL), + SHELL_CMD(download, &download_options, "Download options", NULL), SHELL_SUBCMD_SET_END); + +SHELL_CMD_REGISTER(dl, &sub_dl, "Download client", NULL); diff --git a/subsys/net/lib/downloader/src/transports/coap.c b/subsys/net/lib/downloader/src/transports/coap.c new file mode 100644 index 000000000000..72e427411c67 --- /dev/null +++ b/subsys/net/lib/downloader/src/transports/coap.c @@ -0,0 +1,570 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dl_socket.h" +#include "dl_parse.h" + +LOG_MODULE_DECLARE(downloader, CONFIG_DOWNLOADER_LOG_LEVEL); + +#define DEFAULT_PORT_DTLS 5684 +#define DEFAULT_PORT_UDP 5683 + +#define COAP_VER 1 +#define FILENAME_SIZE CONFIG_DOWNLOADER_MAX_FILENAME_SIZE +#define COAP_PATH_ELEM_DELIM "/" + +struct transport_params_coap { + /** Flag whether config is set */ + bool cfg_set; + /** Configuration options */ + struct downloader_transport_coap_cfg cfg; + /** Initialization status */ + bool initialized; + /** CoAP block context. */ + struct coap_block_context block_ctx; + /** CoAP pending object. */ + struct coap_pending pending; + + struct { + /** Socket descriptor. */ + int fd; + /** Protocol for current download. */ + int proto; + /** Socket type */ + int type; + /** Port */ + uint16_t port; + /** Destination address storage */ + struct sockaddr remote_addr; + } sock; + + /** Request new data */ + bool new_data_req; + /** Request retransmission */ + bool retransmission_req; +}; + +BUILD_ASSERT(CONFIG_DOWNLOADER_TRANSPORT_PARAMS_SIZE >= sizeof(struct transport_params_coap)); + +/* declaration of strtok_r appears to be missing in some cases, + * even though it's defined in the minimal libc, so we forward declare it + */ +extern char *strtok_r(char *str, const char *sep, char **state); + +static int coap_get_current_from_response_pkt(const struct coap_packet *cpkt) +{ + int block = 0; + + block = coap_get_option_int(cpkt, COAP_OPTION_BLOCK2); + if (block < 0) { + return block; + } + + return GET_BLOCK_NUM(block) << (GET_BLOCK_SIZE(block) + 4); +} + +static bool has_pending(struct downloader *dl) +{ + struct transport_params_coap *coap; + + coap = (struct transport_params_coap *)dl->transport_internal; + return coap->pending.timeout > 0; +} + +int coap_block_init(struct downloader *dl, size_t from) +{ + struct transport_params_coap *coap; + + coap = (struct transport_params_coap *)dl->transport_internal; + + coap_block_transfer_init(&coap->block_ctx, coap->cfg.block_size, 0); + coap->block_ctx.current = from; + coap_pending_clear(&coap->pending); + + coap->initialized = true; + return 0; +} + +static int coap_get_recv_timeout(struct downloader *dl, uint32_t *timeout) +{ + int timeo; + struct transport_params_coap *coap; + + coap = (struct transport_params_coap *)dl->transport_internal; + + __ASSERT(has_pending(dl), "Must have coap pending"); + + /* Retransmission is cycled in case recv() times out. In case sending request + * blocks, the time that is used for sending request must be substracted next time + * recv() is called. + */ + timeo = coap->pending.t0 + coap->pending.timeout - k_uptime_get_32(); + if (timeo < 0) { + /* All time is spent when sending request and time this + * method is called, there is no time left for receiving; + * skip over recv() and initiate retransmission on next + * cycle + */ + return -ETIMEDOUT; + } + + *timeout = timeo; + return 0; +} + +int coap_initiate_retransmission(struct downloader *dl) +{ + struct transport_params_coap *coap; + + coap = (struct transport_params_coap *)dl->transport_internal; + + if (coap->pending.timeout == 0) { + return -EINVAL; + } + + if (!coap_pending_cycle(&coap->pending)) { + LOG_ERR("CoAP max-retransmissions exceeded"); + return -1; + } + + return 0; +} + +static int coap_block_update(struct downloader *dl, struct coap_packet *pkt, size_t *blk_off, + bool *more) +{ + struct transport_params_coap *coap; + + coap = (struct transport_params_coap *)dl->transport_internal; + + int err, new_current; + + *blk_off = coap->block_ctx.current % coap_block_size_to_bytes(coap->block_ctx.block_size); + if (*blk_off) { + LOG_DBG("%d bytes of current block already downloaded", *blk_off); + } + + new_current = coap_get_current_from_response_pkt(pkt); + if (new_current < 0) { + LOG_ERR("Failed to get current from CoAP packet, err %d", new_current); + return new_current; + } + + if (new_current < coap->block_ctx.current) { + LOG_WRN("Block out of order %d, expected %d", new_current, coap->block_ctx.current); + return -1; + } else if (new_current > coap->block_ctx.current) { + LOG_WRN("Block out of order %d, expected %d", new_current, coap->block_ctx.current); + return -1; + } + + err = coap_update_from_block(pkt, &coap->block_ctx); + if (err) { + return err; + } + + if (dl->file_size == 0 && coap->block_ctx.total_size > 0) { + LOG_DBG("Total size: %d", coap->block_ctx.total_size); + dl->file_size = coap->block_ctx.total_size; + } + + *more = coap_next_block(pkt, &coap->block_ctx); + if (!*more) { + LOG_DBG("Last block received"); + } + + return 0; +} + +static int coap_parse(struct downloader *dl, size_t len) +{ + int err; + size_t blk_off; + uint8_t response_code; + uint16_t payload_len; + const uint8_t *payload; + struct coap_packet response; + bool more; + struct transport_params_coap *coap; + + coap = (struct transport_params_coap *)dl->transport_internal; + + /* TODO: currently we stop download on every error, but this is mostly not necessary + * and we can just request the same block again using retry mechanism + */ + + err = coap_packet_parse(&response, dl->cfg.buf, len, NULL, 0); + if (err) { + LOG_ERR("Failed to parse CoAP packet, err %d", err); + return -EBADMSG; + } + + if (coap_header_get_id(&response) != coap->pending.id) { + LOG_ERR("Response is not pending"); + return -EBADMSG; + } + + coap_pending_clear(&coap->pending); + + if (coap_header_get_type(&response) != COAP_TYPE_ACK) { + LOG_ERR("Response must be of coap type ACK"); + return -EBADMSG; + } + + response_code = coap_header_get_code(&response); + if (response_code != COAP_RESPONSE_CODE_OK && response_code != COAP_RESPONSE_CODE_CONTENT) { + LOG_ERR("Server responded with code 0x%x", response_code); + return -EBADMSG; + } + + err = coap_block_update(dl, &response, &blk_off, &more); + if (err) { + return -EBADMSG; + } + + payload = coap_packet_get_payload(&response, &payload_len); + if (!payload) { + LOG_WRN("No CoAP payload!"); + return -EBADMSG; + } + + /* Accumulate buffer offset */ + dl->progress += payload_len; + dl->buf_offset = 0; + + dl_transport_evt_data(dl, (void *)payload, payload_len); + + if (!more) { + /* Mark the end, in case we did not know the total size */ + dl->file_size = dl->progress; + } + + coap->new_data_req = true; + return 0; +} + +static int coap_request_send(struct downloader *dl) +{ + int err; + uint16_t id; + char file[FILENAME_SIZE]; + char *path_elem; + char *path_elem_saveptr; + struct coap_packet request; + struct transport_params_coap *coap; + + coap = (struct transport_params_coap *)dl->transport_internal; + + if (has_pending(dl)) { + id = coap->pending.id; + } else { + id = coap_next_id(); + } + + err = coap_packet_init(&request, dl->cfg.buf, dl->cfg.buf_size, COAP_VER, + COAP_TYPE_CON, 8, coap_next_token(), COAP_METHOD_GET, id); + if (err) { + LOG_ERR("Failed to init CoAP message, err %d", err); + return err; + } + + err = dl_parse_url_file(dl->file, file, sizeof(file)); + if (err) { + LOG_ERR("Unable to parse url"); + return err; + } + + path_elem = strtok_r(file, COAP_PATH_ELEM_DELIM, &path_elem_saveptr); + do { + err = coap_packet_append_option(&request, COAP_OPTION_URI_PATH, path_elem, + strlen(path_elem)); + if (err) { + LOG_ERR("Unable add option to request"); + return err; + } + } while ((path_elem = strtok_r(NULL, COAP_PATH_ELEM_DELIM, &path_elem_saveptr))); + + err = coap_append_block2_option(&request, &coap->block_ctx); + if (err) { + LOG_ERR("Unable to add block2 option"); + return err; + } + + err = coap_append_size2_option(&request, &coap->block_ctx); + if (err) { + LOG_ERR("Unable to add size2 option"); + return err; + } + + if (!has_pending(dl)) { + struct coap_transmission_parameters params = coap_get_transmission_parameters(); + + params.max_retransmission = coap->cfg.max_retransmission; + err = coap_pending_init(&coap->pending, &request, &coap->sock.remote_addr, ¶ms); + if (err < 0) { + return -EINVAL; + } + + coap_pending_cycle(&coap->pending); + } + + LOG_DBG("CoAP next block: %d", coap->block_ctx.current); + + err = dl_socket_send_timeout_set(coap->sock.fd, coap->pending.timeout); + if (err) { + return err; + } + + err = dl_socket_send(coap->sock.fd, dl->cfg.buf, request.offset); + if (err) { + LOG_ERR("Failed to send CoAP request, errno %d", errno); + return err; + } + + if (IS_ENABLED(CONFIG_DOWNLOADER_LOG_HEADERS)) { + LOG_HEXDUMP_DBG(request.data, request.offset, "CoAP request"); + } + + return 0; +} + +static bool dl_coap_proto_supported(struct downloader *dl, const char *url) +{ + if (strncmp(url, "coaps://", 8) == 0) { + return true; + } else if (strncmp(url, "coap://", 7) == 0) { + return true; + } + + return false; +} + +static int dl_coap_init(struct downloader *dl, struct downloader_host_cfg *dl_host_cfg, + const char *url) +{ + int err; + struct transport_params_coap *coap; + + coap = (struct transport_params_coap *)dl->transport_internal; + + /* Reset coap internal struct except config. */ + struct downloader_transport_coap_cfg tmp_cfg = coap->cfg; + bool cfg_set = coap->cfg_set; + + memset(coap, 0, sizeof(struct transport_params_coap)); + + if (cfg_set) { + coap->cfg = tmp_cfg; + coap->cfg_set = cfg_set; + } else { + coap->cfg.block_size = 5; + coap->cfg.max_retransmission = 4; + } + + coap->sock.proto = IPPROTO_UDP; + coap->sock.type = SOCK_DGRAM; + + if (strncmp(url, "coaps://", 8) == 0 || + (dl_host_cfg->sec_tag_count != 0 && dl_host_cfg->sec_tag_list != NULL)) { + coap->sock.proto = IPPROTO_DTLS_1_2; + coap->sock.type = SOCK_DGRAM; + + if (dl_host_cfg->sec_tag_list == NULL || dl_host_cfg->sec_tag_count == 0) { + LOG_WRN("No security tag provided for TLS/DTLS"); + return -EINVAL; + } + } + + err = dl_parse_url_port(url, &coap->sock.port); + if (err) { + switch (coap->sock.proto) { + case IPPROTO_DTLS_1_2: + coap->sock.port = DEFAULT_PORT_DTLS; + break; + case IPPROTO_UDP: + coap->sock.port = DEFAULT_PORT_UDP; + break; + } + LOG_DBG("Port not specified, using default: %d", coap->sock.port); + } + + if (dl_host_cfg->set_native_tls) { + LOG_DBG("Enabled native TLS"); + coap->sock.type |= SOCK_NATIVE_TLS; + } + + return 0; +} + +static int dl_coap_deinit(struct downloader *dl) +{ + struct transport_params_coap *coap; + + coap = (struct transport_params_coap *)dl->transport_internal; + + if (coap->sock.fd != -1) { + dl_socket_close(&coap->sock.fd); + } + + return 0; +} + +static int dl_coap_connect(struct downloader *dl) +{ + int err; + struct transport_params_coap *coap; + + coap = (struct transport_params_coap *)dl->transport_internal; + + err = -1; + + err = dl_socket_configure_and_connect(&coap->sock.fd, coap->sock.proto, coap->sock.type, + coap->sock.port, &coap->sock.remote_addr, + dl->hostname, &dl->host_cfg); + if (err) { + goto cleanup; + } + + coap_block_init(dl, dl->progress); + +cleanup: + if (err) { + /* Unable to connect, close socket */ + dl_socket_close(&coap->sock.fd); + return err; + } + + coap->new_data_req = true; + + return err; +} + +static int dl_coap_close(struct downloader *dl) +{ + int err; + struct transport_params_coap *coap; + + coap = (struct transport_params_coap *)dl->transport_internal; + + if (coap->sock.fd != -1) { + err = dl_socket_close(&coap->sock.fd); + return err; + } + + return -EBADF; +} + +static int dl_coap_download(struct downloader *dl) +{ + int ret, len, timeout; + struct transport_params_coap *coap; + + coap = (struct transport_params_coap *)dl->transport_internal; + + if (coap->new_data_req) { + /* Request next fragment */ + dl->buf_offset = 0; + ret = coap_request_send(dl); + if (ret) { + LOG_DBG("data_req failed, err %d", ret); + /** Attempt reconnection. */ + return ret; + } + + coap->new_data_req = false; + } + + if (coap->retransmission_req) { + dl->buf_offset = 0; + ret = coap_initiate_retransmission(dl); + if (ret) { + LOG_DBG("retransmission_req failed, err %d", ret); + /** Attempt reconnection. */ + return -ECONNRESET; + } + + coap->retransmission_req = false; + } + + __ASSERT(dl->buf_offset < dl->cfg.buf_size, "Buffer overflow"); + + LOG_DBG("Receiving up to %d bytes at %p...", (dl->cfg.buf_size - dl->buf_offset), + (void *)(dl->cfg.buf + dl->buf_offset)); + + ret = coap_get_recv_timeout(dl, &timeout); + if (ret) { + LOG_DBG("CoAP timeout"); + return -ETIMEDOUT; + } + + ret = dl_socket_recv_timeout_set(coap->sock.fd, timeout); + if (ret) { + LOG_DBG("Failed to set CoAP recv timeout, err %d", ret); + return ret; + } + + len = dl_socket_recv(coap->sock.fd, dl->cfg.buf + dl->buf_offset, + dl->cfg.buf_size - dl->buf_offset); + if (len < 0) { + if ((len == ETIMEDOUT) || (len == EWOULDBLOCK) || (len == EAGAIN)) { + /* Request data again */ + coap->retransmission_req = true; + return 0; + } + + return len; + } + + ret = coap_parse(dl, len); + if (ret < 0) { + /* Request data again */ + coap->retransmission_req = true; + return 0; + } + + if (dl->progress == dl->file_size) { + dl->complete = true; + } + + return 0; +} + +static const struct dl_transport dl_transport_coap = { + .proto_supported = dl_coap_proto_supported, + .init = dl_coap_init, + .deinit = dl_coap_deinit, + .connect = dl_coap_connect, + .close = dl_coap_close, + .download = dl_coap_download, +}; + +DL_TRANSPORT(coap, &dl_transport_coap); + +int downloader_transport_coap_set_config(struct downloader *dl, + struct downloader_transport_coap_cfg *cfg) +{ + struct transport_params_coap *coap; + + if (!dl || !cfg) { + return -EINVAL; + } + + coap = (struct transport_params_coap *)dl->transport_internal; + coap->cfg = *cfg; + + coap->cfg_set = true; + + return 0; +} diff --git a/subsys/net/lib/downloader/src/transports/http.c b/subsys/net/lib/downloader/src/transports/http.c new file mode 100644 index 000000000000..439422ea1702 --- /dev/null +++ b/subsys/net/lib/downloader/src/transports/http.c @@ -0,0 +1,636 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dl_socket.h" +#include "dl_parse.h" + +LOG_MODULE_DECLARE(downloader, CONFIG_DOWNLOADER_LOG_LEVEL); + +/* nRF91 modem TLS secure socket buffer limited to 2kB including header */ +#define TLS_RANGE_MAX 2048 + +#define DEFAULT_PORT_TLS 443 +#define DEFAULT_PORT_TCP 80 + +#define HTTP_RESPONSE_OK 200 +#define HTTP_RESPONSE_PARTIAL_CONTENT 206 +#define HTTP_RESPONSE_MOVED_PERMANENTLY 301 +#define HTTP_RESPONSE_FOUND 302 +#define HTTP_RESPONSE_SEE_OTHER 303 +#define HTTP_RESPONSE_TEMPORARY_REDIRECT 307 +#define HTTP_RESPONSE_PERMANENT_REDIRECT 308 + +#define HTTP "http://" +#define HTTPS "https://" + +/* Request whole file; use with HTTP */ +#define HTTP_GET \ + "GET /%s HTTP/1.1\r\n" \ + "Host: %s\r\n" \ + "Connection: keep-alive\r\n" \ + "\r\n" + +/* Request remaining bytes from offset; use with HTTP */ +#define HTTP_GET_OFFSET \ + "GET /%s HTTP/1.1\r\n" \ + "Host: %s\r\n" \ + "Range: bytes=%u-\r\n" \ + "Connection: keep-alive\r\n" \ + "\r\n" + +/* Request a range of bytes; use with HTTPS due to modem limitations */ +#define HTTP_GET_RANGE \ + "GET /%s HTTP/1.1\r\n" \ + "Host: %s\r\n" \ + "Range: bytes=%u-%u\r\n" \ + "Connection: keep-alive\r\n" \ + "\r\n" + +struct transport_params_http { + /** Configuration options */ + struct downloader_transport_http_cfg cfg; + /** The server has closed the connection. */ + bool connection_close; + /** Is using ranged query. */ + bool ranged; + /** Ranged progress */ + size_t ranged_progress; + /** HTTP header */ + struct { + /** Header length */ + size_t hdr_len; + /** Status code */ + unsigned long status_code; + /** Whether the HTTP header for + * the current fragment has been processed. + */ + bool has_end; + } header; + + struct { + /** Socket descriptor. */ + int fd; + /** Protocol for current download. */ + int proto; + /** Socket type */ + int type; + /** Port */ + uint16_t port; + /** Destination address storage */ + struct sockaddr remote_addr; + } sock; + + /** Request new data */ + bool new_data_req; +}; + +BUILD_ASSERT(CONFIG_DOWNLOADER_TRANSPORT_PARAMS_SIZE >= sizeof(struct transport_params_http)); + +/* Include size flavour of strstr for safety. */ +#if defined(CONFIG_EXTERNAL_LIBC) +/* Pull in memmem and strnstr due to being an extension to the C library and + * not included by default. + */ +extern void *memmem(const void *haystack, size_t hs_len, const void *needle, size_t ne_len); +extern size_t strnlen(const char *s, size_t maxlen); +static char *strnstr(const char *haystack, const char *needle, size_t haystack_len) +{ + char *x; + + if (!haystack || !needle) { + return NULL; + } + size_t needle_len = strnlen(needle, haystack_len); + + if (needle_len < haystack_len || !needle[needle_len]) { + x = memmem(haystack, haystack_len, needle, needle_len); + if (x && !memchr(haystack, 0, x - haystack)) { + return x; + } + } + + return NULL; +} +#else +extern char *strnstr(const char *haystack, const char *needle, size_t haystack_sz); +#endif + +static int http_get_request_send(struct downloader *dl) +{ + int err; + int len; + size_t off = 0; + bool tls_force_range; + struct transport_params_http *http; + + http = (struct transport_params_http *)dl->transport_internal; + + http->header.has_end = false; + + /* nRF91 series has a limitation of decoding ~2k of data at once when using TLS */ + tls_force_range = (http->sock.proto == IPPROTO_TLS_1_2 && !dl->host_cfg.set_native_tls && + IS_ENABLED(CONFIG_SOC_SERIES_NRF91X)); + + if (dl->host_cfg.range_override) { + if (tls_force_range && dl->host_cfg.range_override > (TLS_RANGE_MAX - 1)) { + LOG_WRN("Range override > TLS max range, setting to TLS max range"); + dl->host_cfg.range_override = (TLS_RANGE_MAX - 1); + } + } else if (tls_force_range) { + dl->host_cfg.range_override = TLS_RANGE_MAX - 1; + } + + if (dl->host_cfg.range_override) { + off = dl->progress + dl->host_cfg.range_override; + + if (dl->file_size && (off > dl->file_size - 1)) { + /* Don't request bytes past the end of file */ + off = dl->file_size - 1; + } + + len = snprintf(dl->cfg.buf, dl->cfg.buf_size, HTTP_GET_RANGE, dl->file, + dl->hostname, dl->progress, off); + http->ranged = true; + http->ranged_progress = 0; + LOG_DBG("Range request up to %d bytes", dl->host_cfg.range_override); + goto send; + } else if (dl->progress) { + len = snprintf(dl->cfg.buf, dl->cfg.buf_size, HTTP_GET_OFFSET, dl->file, + dl->hostname, dl->progress); + http->ranged = false; + } else { + len = snprintf(dl->cfg.buf, dl->cfg.buf_size, HTTP_GET, dl->file, + dl->hostname); + http->ranged = false; + } + +send: + if (len < 0 || len > dl->cfg.buf_size) { + LOG_ERR("Cannot create GET request, buffer too small"); + return -ENOMEM; + } + + if (IS_ENABLED(CONFIG_DOWNLOADER_LOG_HEADERS)) { + LOG_HEXDUMP_DBG(dl->cfg.buf, len, "HTTP request"); + } + + LOG_DBG("http request:\n%s", dl->cfg.buf); + + err = dl_socket_send(http->sock.fd, dl->cfg.buf, len); + if (err) { + LOG_ERR("Failed to send HTTP request, errno %d", errno); + return err; + } + + return 0; +} + +/* Returns: + * Number of bytes parsed on success. + * Negative errno on error. + */ +static int http_header_parse(struct downloader *dl, size_t buf_len) +{ + int err; + char *p; + char *q; + size_t parse_len; + unsigned int expected_status; + struct transport_params_http *http; + + http = (struct transport_params_http *)dl->transport_internal; + + LOG_DBG("(partial) http header response:\n%s", dl->cfg.buf); + + p = strnstr(dl->cfg.buf, "\r\n\r\n", dl->cfg.buf_size); + if (p) { + /* End of header received */ + http->header.has_end = true; + parse_len = p + strlen("\r\n\r\n") - (char *)dl->cfg.buf; + } else { + parse_len = buf_len; + } + + for (size_t i = 0; i < parse_len; i++) { + dl->cfg.buf[i] = tolower(dl->cfg.buf[i]); + } + + /* Look for the status code just after "http/1.1 " */ + p = strnstr(dl->cfg.buf, "http/1.1 ", parse_len); + if (p) { + q = strnstr(p, "\r\n", parse_len - (p - dl->cfg.buf)); + if (q) { + /* Received entire line */ + p += strlen("http/1.1 "); + http->header.status_code = strtoul(p, &q, 10); + } + } + + if (http->header.status_code == HTTP_RESPONSE_MOVED_PERMANENTLY || + http->header.status_code == HTTP_RESPONSE_FOUND || + http->header.status_code == HTTP_RESPONSE_SEE_OTHER || + http->header.status_code == HTTP_RESPONSE_TEMPORARY_REDIRECT || + http->header.status_code == HTTP_RESPONSE_PERMANENT_REDIRECT) { + /* Resource is moved, update host and file before reconnecting. */ + p = strnstr(dl->cfg.buf, "\r\nlocation:", parse_len); + if (p) { + q = strnstr((p + 1), "\r\n", parse_len - ((p + 1) - dl->cfg.buf)); + if (q) { + + /* Received entire line */ + p += strlen("\r\nlocation:"); + *q = '\0'; + + LOG_INF("Resource moved to %s", p); + + err = dl_parse_url_host(p, dl->hostname, sizeof(dl->hostname)); + if (err) { + LOG_ERR("Failed to parse hostname, err %d, url %s", err, p); + return -EBADMSG; + } + + err = dl_parse_url_file(p, dl->file, sizeof(dl->file)); + if (err) { + LOG_ERR("Failed to parse filename, err %d, url %s", err, p); + k_mutex_unlock(&dl->mutex); + return -EBADMSG; + } + + return -ECONNRESET; + } + } + } + + /* The file size is returned via "Content-Length" in case of HTTP, + * and via "Content-Range" in case of HTTPS with range requests. + */ + do { + if (dl->file_size == 0) { + if (http->ranged) { + p = strnstr(dl->cfg.buf, "\r\ncontent-range", parse_len); + if (!p) { + break; + } + p = strnstr(p, "/", parse_len - (p - dl->cfg.buf)); + if (!p) { + break; + } + q = strnstr(p, "\r\n", parse_len - (p - dl->cfg.buf)); + if (!q) { + /* Missing end of line */ + break; + } + } else { /* proto == PROTO_HTTP */ + p = strnstr(dl->cfg.buf, "\r\ncontent-length", parse_len); + if (!p) { + break; + } + p = strstr(p, ":"); + if (!p) { + break; + } + q = strnstr(p, "\r\n", parse_len - (p - dl->cfg.buf)); + if (!q) { + /* Missing end of line */ + break; + } + /* Accumulate any eventual progress (starting offset) + * when reading the file size from Content-Length + */ + dl->file_size = dl->progress; + } + + dl->file_size += atoi(p + 1); + LOG_DBG("File size = %u", dl->file_size); + } + } while (0); + + p = strnstr(dl->cfg.buf, "\r\nconnection: close", parse_len); + if (p) { + LOG_WRN("Peer closed connection, will re-connect"); + http->connection_close = true; + } + + if (http->header.has_end) { + /* We have received the end of the header. + * Verify that we have received everything that we need. + */ + + if (!http->header.status_code) { + LOG_ERR("Server response malformed: status code not found"); + return -EBADMSG; + } + + expected_status = (http->ranged || dl->progress) ? HTTP_RESPONSE_PARTIAL_CONTENT : + HTTP_RESPONSE_OK; + if (http->header.status_code != expected_status) { + LOG_ERR("Unexpected HTTP response code %ld", http->header.status_code); + return -EBADMSG; + } + + if (!dl->file_size) { + LOG_ERR("File size not set"); + return -EBADMSG; + } + + return parse_len; + } + + q = dl->cfg.buf + buf_len; + /* We are still missing part of the header. + * Return the lines (in number of bytes) that we have parsed. + */ + while (q > dl->cfg.buf && (*q != '\r') && (*q != '\n')) { + q--; + } + + /* Keep \r and \n in the buffer in case it is part of the header ending. */ + while (*(q - 1) == '\r' || *(q - 1) == '\n') { + q--; + } + + parse_len = (q - dl->cfg.buf); + + return parse_len; +} + +/* Returns: + * Length of data payload left to process on success + * Negative errno on error. + */ +static int http_parse(struct downloader *dl, size_t len) +{ + int parsed_len; + struct transport_params_http *http; + + http = (struct transport_params_http *)dl->transport_internal; + + if (!http->header.has_end) { + /* Parse what we can from the header */ + parsed_len = http_header_parse(dl, len); + if (parsed_len < 0) { + /* Something is wrong with the header */ + return parsed_len; + } + + if (parsed_len == len) { + dl->buf_offset = 0; + return 0; + } else if (parsed_len) { + /* Keep remaining payload */ + len = len - parsed_len; + memmove(dl->cfg.buf, dl->cfg.buf + parsed_len, len); + dl->buf_offset = len; + } + + if (!http->header.has_end) { + if (dl->cfg.buf_size == dl->buf_offset) { + LOG_ERR("Could not parse HTTP header lines from server (> %d)", + dl->cfg.buf_size); + return -E2BIG; + } + /* Wait for rest of header */ + return 0; + } + } + + /* Have we received a whole fragment or the whole file? */ + if (dl->progress + len != dl->file_size) { + if (http->ranged) { + http->ranged_progress += len; + if (http->ranged_progress < (dl->host_cfg.range_override + ? dl->host_cfg.range_override + : TLS_RANGE_MAX - 1)) { + /* Ranged query: read until a full fragment */ + return len; + } + } else { + /* Non-ranged query: just keep on reading, ignore fragment size */ + return len; + } + } + + /* Either we have a full file, or we need to request a next fragment */ + http->new_data_req = true; + return len; +} + +static bool dl_http_proto_supported(struct downloader *dl, const char *url) +{ + if (strncmp(url, HTTPS, strlen(HTTPS)) == 0) { + return true; + } + + if (strncmp(url, HTTP, strlen(HTTP)) == 0) { + return true; + } + + return false; +} + +static int dl_http_init(struct downloader *dl, struct downloader_host_cfg *dl_host_cfg, + const char *url) +{ + int err; + struct transport_params_http *http; + + http = (struct transport_params_http *)dl->transport_internal; + + /* Reset http internal struct except config. */ + struct downloader_transport_http_cfg tmp_cfg = http->cfg; + + memset(http, 0, sizeof(struct transport_params_http)); + http->cfg = tmp_cfg; + + http->sock.proto = IPPROTO_TCP; + http->sock.type = SOCK_STREAM; + + if (strncmp(url, HTTPS, strlen(HTTPS)) == 0 || + (strncmp(url, HTTP, strlen(HTTP)) != 0 && + (dl_host_cfg->sec_tag_count != 0 && dl_host_cfg->sec_tag_list != NULL))) { + http->sock.proto = IPPROTO_TLS_1_2; + http->sock.type = SOCK_STREAM; + + if (dl_host_cfg->sec_tag_list == NULL || dl_host_cfg->sec_tag_count == 0) { + LOG_WRN("No security tag provided for TLS/DTLS"); + return -EINVAL; + } + } + + err = dl_parse_url_port(url, &http->sock.port); + if (err) { + switch (http->sock.proto) { + case IPPROTO_TLS_1_2: + http->sock.port = DEFAULT_PORT_TLS; + break; + case IPPROTO_TCP: + http->sock.port = DEFAULT_PORT_TCP; + break; + } + LOG_DBG("Port not specified, using default: %d", http->sock.port); + } + + if (dl_host_cfg->set_native_tls) { + LOG_DBG("Enabled native TLS"); + http->sock.type |= SOCK_NATIVE_TLS; + } + + return 0; +} + +static int dl_http_deinit(struct downloader *dl) +{ + struct transport_params_http *http; + + http = (struct transport_params_http *)dl->transport_internal; + + if (http->sock.fd != -1) { + dl_socket_close(&http->sock.fd); + } + + return 0; +} + +static int dl_http_connect(struct downloader *dl) +{ + int err; + struct transport_params_http *http; + + http = (struct transport_params_http *)dl->transport_internal; + + err = -1; + + err = dl_socket_configure_and_connect(&http->sock.fd, http->sock.proto, http->sock.type, + http->sock.port, &http->sock.remote_addr, + dl->hostname, &dl->host_cfg); + if (err) { + return err; + } + + err = dl_socket_recv_timeout_set(http->sock.fd, http->cfg.sock_recv_timeo_ms); + if (err) { + /* Unable to set timeout, close socket */ + LOG_ERR("Failed to set http recv timeout, err %d", err); + dl_socket_close(&http->sock.fd); + return err; + } + + http->connection_close = false; + http->new_data_req = true; + + return err; +} + +static int dl_http_close(struct downloader *dl) +{ + int err; + struct transport_params_http *http; + + http = (struct transport_params_http *)dl->transport_internal; + + if (http->sock.fd != -1) { + err = dl_socket_close(&http->sock.fd); + return err; + } + + memset(&http->sock.remote_addr, 0, sizeof(http->sock.remote_addr)); + + return -EBADF; +} + +static int dl_http_download(struct downloader *dl) +{ + int ret, len; + struct transport_params_http *http; + + http = (struct transport_params_http *)dl->transport_internal; + + if (http->new_data_req) { + /* Request next fragment */ + dl->buf_offset = 0; + ret = http_get_request_send(dl); + if (ret) { + LOG_DBG("data_req failed, err %d", ret); + /** Attempt reconnection. */ + return -ECONNRESET; + } + + http->new_data_req = false; + } + + __ASSERT(dl->buf_offset < dl->cfg.buf_size, "Buffer overflow"); + + LOG_DBG("Receiving up to %d bytes at %p...", (dl->cfg.buf_size - dl->buf_offset), + (void *)(dl->cfg.buf + dl->buf_offset)); + + len = dl_socket_recv(http->sock.fd, dl->cfg.buf + dl->buf_offset, + dl->cfg.buf_size - dl->buf_offset); + + if (len < 0) { + if (http->connection_close) { + return -ECONNRESET; + } + + return len; + } + + if (len == 0) { + return -ECONNRESET; + } + + ret = http_parse(dl, len); + if (ret <= 0) { + return ret; + } + + if (http->header.has_end) { + /* Accumulate progress */ + dl->progress += ret; + dl_transport_evt_data(dl, dl->cfg.buf, ret); + if (dl->progress == dl->file_size) { + dl->complete = true; + } + dl->buf_offset = 0; + } + + return 0; +} + +static const struct dl_transport dl_transport_http = { + .proto_supported = dl_http_proto_supported, + .init = dl_http_init, + .deinit = dl_http_deinit, + .connect = dl_http_connect, + .close = dl_http_close, + .download = dl_http_download, +}; + +DL_TRANSPORT(http, &dl_transport_http); + +int downloader_transport_http_set_config(struct downloader *dl, + struct downloader_transport_http_cfg *cfg) +{ + struct transport_params_http *http; + + if (!dl || !cfg) { + return -EINVAL; + } + + http = (struct transport_params_http *)dl->transport_internal; + http->cfg = *cfg; + + return 0; +} diff --git a/subsys/net/lib/fota_download/CMakeLists.txt b/subsys/net/lib/fota_download/CMakeLists.txt index 5f11d17efd40..eec5e8374d07 100644 --- a/subsys/net/lib/fota_download/CMakeLists.txt +++ b/subsys/net/lib/fota_download/CMakeLists.txt @@ -28,5 +28,5 @@ zephyr_library_sources_ifdef(CONFIG_DFU_TARGET_SMP zephyr_include_directories(./include) zephyr_include_directories_ifdef(CONFIG_SECURE_BOOT ${ZEPHYR_NRF_MODULE_DIR}/subsys/dfu/include) -zephyr_include_directories_ifdef(CONFIG_DOWNLOAD_CLIENT - ${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/download_client/include) +zephyr_include_directories_ifdef(CONFIG_DOWNLOADER + ${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/downloader/include) diff --git a/subsys/net/lib/fota_download/Kconfig b/subsys/net/lib/fota_download/Kconfig index 28c702d330b9..5c99846fa9a8 100644 --- a/subsys/net/lib/fota_download/Kconfig +++ b/subsys/net/lib/fota_download/Kconfig @@ -6,12 +6,12 @@ menuconfig FOTA_DOWNLOAD bool "FOTA Download" - depends on DOWNLOAD_CLIENT + depends on DOWNLOADER depends on DFU_TARGET select SYS_HASH_FUNC32 imply FW_INFO -if (FOTA_DOWNLOAD) +if FOTA_DOWNLOAD config FOTA_SOCKET_RETRIES int "Number of retries for socket-related download issues" @@ -31,6 +31,10 @@ config FOTA_DOWNLOAD_MCUBOOT_FLASH_BUF_SZ help Buffer size must be aligned to the minimal flash write block size +config FOTA_DOWNLOAD_BUF_SZ + int "Size of buffer used for downloader library" + default 2048 + config FOTA_DOWNLOAD_FULL_MODEM_BUF_SZ int "Size of buffer used for flash write operations during full modem updates" depends on DFU_TARGET_FULL_MODEM @@ -71,4 +75,4 @@ module-dep=LOG module-str=Firmware Over the Air Download source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config" -endif # FOTA_DOWNLOAD +endif #FOTA_DOWNLOAD diff --git a/subsys/net/lib/fota_download/src/fota_download.c b/subsys/net/lib/fota_download/src/fota_download.c index 96aaa11cc618..b206124c424e 100644 --- a/subsys/net/lib/fota_download/src/fota_download.c +++ b/subsys/net/lib/fota_download/src/fota_download.c @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include @@ -35,10 +35,19 @@ static const char *dl_host; static const char *dl_file; static uint32_t dl_host_hash; static uint32_t dl_file_hash; -static struct download_client dlc; + +static struct downloader dl; +static int downloader_callback(const struct downloader_evt *event); +static char dl_buf[CONFIG_FOTA_DOWNLOAD_BUF_SZ]; +static struct downloader_cfg dl_cfg = { + .callback = downloader_callback, + .buf = dl_buf, + .buf_size = sizeof(dl_buf), +}; +static struct downloader_host_cfg dl_host_cfg; /** SMP MCUBoot image type */ static bool use_smp_dfu_target; -static struct k_work_delayable dlc_with_offset_work; +static struct k_work_delayable dl_with_offset_work; static int socket_retries_left; #ifdef CONFIG_DFU_TARGET_MCUBOOT static uint8_t mcuboot_buf[CONFIG_FOTA_DOWNLOAD_MCUBOOT_FLASH_BUF_SZ] __aligned(4); @@ -50,7 +59,7 @@ enum flags_t { FLAG_FIRST_FRAGMENT, FLAG_RESUME, FLAG_NEW_URI, - FLAG_CLOSED, + FLAG_STOPPED, FLAG_CANCEL, }; static atomic_t flags; @@ -138,7 +147,7 @@ static size_t file_size_get(size_t *size) *size = ext_file_sz; return 0; #endif - return download_client_file_size_get(&dlc, size); + return downloader_file_size_get(&dl, size); } static size_t downloaded_size_get(size_t *size) @@ -147,18 +156,18 @@ static size_t downloaded_size_get(size_t *size) *size = ext_rcvd_sz; return 0; #endif - return download_client_downloaded_size_get(&dlc, size); + return downloader_downloaded_size_get(&dl, size); } -static int disconnect(void) +static int dl_cancel(void) { #if defined(CONFIG_FOTA_DOWNLOAD_EXTERNAL_DL) return 0; #endif - return download_client_disconnect(&dlc); + return downloader_cancel(&dl); } -static int download_client_callback(const struct download_client_evt *event) +static int downloader_callback(const struct downloader_evt *event) { static size_t file_size; size_t offset; @@ -169,7 +178,7 @@ static int download_client_callback(const struct download_client_evt *event) } switch (event->id) { - case DOWNLOAD_CLIENT_EVT_FRAGMENT: { + case DOWNLOADER_EVT_FRAGMENT: { if (atomic_test_and_clear_bit(&flags, FLAG_FIRST_FRAGMENT)) { err = file_size_get(&file_size); if (err != 0) { @@ -245,10 +254,9 @@ static int download_client_callback(const struct download_client_evt *event) * schedule new download from offset. */ atomic_set_bit(&flags, FLAG_RESUME); - (void)disconnect(); - k_work_schedule(&dlc_with_offset_work, K_SECONDS(1)); + (void)dl_cancel(); + k_work_schedule(&dl_with_offset_work, K_SECONDS(1)); LOG_INF("Refuse fragment, restart with offset"); - return -1; } } else { @@ -291,7 +299,7 @@ static int download_client_callback(const struct download_client_evt *event) break; } - case DOWNLOAD_CLIENT_EVT_DONE: + case DOWNLOADER_EVT_DONE: err = dfu_target_done(true); if (err == 0 && IS_ENABLED(CONFIG_FOTA_CLIENT_AUTOSCHEDULE_UPDATE)) { err = dfu_target_schedule_update(0); @@ -303,14 +311,13 @@ static int download_client_callback(const struct download_client_evt *event) goto error_and_close; } - err = disconnect(); - if (err != 0) { - set_error_state(FOTA_DOWNLOAD_ERROR_CAUSE_INTERNAL); - goto error_and_close; - } + atomic_clear_bit(&flags, FLAG_DOWNLOADING); + atomic_set_bit(&flags, FLAG_STOPPED); + send_evt(FOTA_DOWNLOAD_EVT_FINISHED); + break; - case DOWNLOAD_CLIENT_EVT_ERROR: + case DOWNLOADER_EVT_ERROR: /* In case of socket errors we can return 0 to retry/continue, * or non-zero to stop */ @@ -319,15 +326,17 @@ static int download_client_callback(const struct download_client_evt *event) socket_retries_left); socket_retries_left--; /* Fall through and return 0 below to tell - * download_client to retry + * downloader to retry */ - } else if ((event->error == -ECONNABORTED) || (event->error == -ECONNREFUSED)) { + } else if ((event->error == -ECONNABORTED) || + (event->error == -ECONNREFUSED) || + (event->error == -EHOSTUNREACH)) { LOG_ERR("Download client failed to connect to server"); set_error_state(FOTA_DOWNLOAD_ERROR_CAUSE_CONNECT_FAILED); goto error_and_close; } else { - LOG_ERR("Download client error"); + LOG_ERR("Downloader error event %d", event->error); err = dfu_target_done(false); if (err == -EACCES) { LOG_DBG("No DFU target was initialized"); @@ -339,13 +348,15 @@ static int download_client_callback(const struct download_client_evt *event) goto error_and_close; } break; - case DOWNLOAD_CLIENT_EVT_CLOSED: - atomic_set_bit(&flags, FLAG_CLOSED); + case DOWNLOADER_EVT_STOPPED: + atomic_set_bit(&flags, FLAG_STOPPED); /* Only clear flags if we are not going to resume */ if (!atomic_test_bit(&flags, FLAG_RESUME)) { stopped(); } break; + case DOWNLOADER_EVT_DEINITIALIZED: + /* Not implemented in fota download */ default: break; } @@ -354,7 +365,6 @@ static int download_client_callback(const struct download_client_evt *event) error_and_close: atomic_clear_bit(&flags, FLAG_RESUME); - (void)disconnect(); dfu_target_done(false); return -1; } @@ -366,7 +376,7 @@ static int get_from_offset(const size_t offset) return 0; } - int err = download_client_get(&dlc, dl_host, &dlc.config, dl_file, offset); + int err = downloader_get_with_host_and_file(&dl, &dl_host_cfg, dl_host, dl_file, offset); if (err != 0) { LOG_ERR("%s failed to start download with error %d", __func__, err); @@ -383,9 +393,9 @@ static void download_with_offset(struct k_work *unused) size_t offset; int err; - if (!atomic_test_bit(&flags, FLAG_CLOSED)) { - /* Re-schedule, wait for socket close */ - k_work_schedule(&dlc_with_offset_work, K_SECONDS(1)); + if (!atomic_test_bit(&flags, FLAG_STOPPED)) { + /* Re-schedule, wait for previous download to be stopped */ + k_work_schedule(&dl_with_offset_work, K_SECONDS(1)); return; } @@ -407,20 +417,6 @@ static void download_with_offset(struct k_work *unused) stop_and_clear_flags: stopped(); - return; -} - -static bool is_ip_address(const char *host) -{ - struct sockaddr sa; - - if (zsock_inet_pton(AF_INET, host, sa.data) == 1) { - return true; - } else if (zsock_inet_pton(AF_INET6, host, sa.data) == 1) { - return true; - } - - return false; } int fota_download_b1_file_parse(char *s0_s1_files) @@ -509,17 +505,19 @@ int fota_download_s0_active_get(bool *const s0_active) int fota_download_any(const char *host, const char *file, const int *sec_tag_list, uint8_t sec_tag_count, uint8_t pdn_id, size_t fragment_size) { - return fota_download(host, file, sec_tag_list, sec_tag_count, pdn_id, - fragment_size, DFU_TARGET_IMAGE_TYPE_ANY); + return fota_download(host, file, sec_tag_list, sec_tag_count, pdn_id, fragment_size, + DFU_TARGET_IMAGE_TYPE_ANY); } static void set_host_and_file(char const *const host, char const *const file) { - uint32_t host_hash = sys_hash32(host, strlen(host)); - uint32_t file_hash = sys_hash32(file, strlen(file)); + uint32_t host_hash; + uint32_t file_hash; + + host_hash = sys_hash32(host, strlen(host)); + file_hash = sys_hash32(file, strlen(file)); - LOG_DBG("URI checksums %d,%d,%d,%d\r\n", host_hash, file_hash, - dl_host_hash, dl_file_hash); + LOG_DBG("URI checksums %d,%d,%d,%d\r\n", host_hash, file_hash, dl_host_hash, dl_file_hash); /* Verify if the URI is same as last time, if not, prevent resuming. */ if (dl_host_hash != host_hash || dl_file_hash != file_hash) { @@ -536,9 +534,9 @@ static void set_host_and_file(char const *const host, char const *const file) } #if defined(CONFIG_FOTA_DOWNLOAD_EXTERNAL_DL) -int fota_download_external_evt_handle(struct download_client_evt const *const evt) +int fota_download_external_evt_handle(struct downloader_evt const *const evt) { - return download_client_callback(evt); + return downloader_callback(evt); } int fota_download_external_start(const char *host, const char *file, @@ -553,7 +551,7 @@ int fota_download_external_start(const char *host, const char *file, return -EALREADY; } - atomic_clear_bit(&flags, FLAG_CLOSED); + atomic_clear_bit(&flags, FLAG_STOPPED); atomic_clear_bit(&flags, FLAG_RESUME); set_error_state(FOTA_DOWNLOAD_ERROR_CAUSE_NO_ERROR); @@ -587,16 +585,14 @@ int fota_download(const char *host, const char *file, int err; static int sec_tag_list_copy[CONFIG_FOTA_DOWNLOAD_SEC_TAG_LIST_SIZE_MAX]; - struct download_client_cfg config = { - .pdn_id = pdn_id, - .frag_size_override = fragment_size, - }; + dl_host_cfg.pdn_id = pdn_id; + dl_host_cfg.range_override = fragment_size; if (sec_tag_count > ARRAY_SIZE(sec_tag_list_copy)) { return -E2BIG; } - atomic_clear_bit(&flags, FLAG_CLOSED); + atomic_clear_bit(&flags, FLAG_STOPPED); atomic_clear_bit(&flags, FLAG_RESUME); set_error_state(FOTA_DOWNLOAD_ERROR_CAUSE_NO_ERROR); @@ -605,12 +601,8 @@ int fota_download(const char *host, const char *file, if ((sec_tag_list != NULL) && (sec_tag_count > 0)) { memcpy(sec_tag_list_copy, sec_tag_list, sec_tag_count * sizeof(sec_tag_list[0])); - config.sec_tag_count = sec_tag_count; - config.sec_tag_list = sec_tag_list_copy; - - if (!is_ip_address(host)) { - config.set_tls_hostname = true; - } + dl_host_cfg.sec_tag_count = sec_tag_count; + dl_host_cfg.sec_tag_list = sec_tag_list_copy; } socket_retries_left = CONFIG_FOTA_SOCKET_RETRIES; @@ -636,18 +628,18 @@ int fota_download(const char *host, const char *file, atomic_set_bit(&flags, FLAG_FIRST_FRAGMENT); - err = download_client_get(&dlc, dl_host, &config, dl_file, 0); + err = downloader_get_with_host_and_file(&dl, &dl_host_cfg, dl_host, dl_file, 0); if (err != 0) { atomic_clear_bit(&flags, FLAG_DOWNLOADING); - (void)disconnect(); + (void)dl_cancel(); return err; } return 0; } -int fota_download_start(const char *host, const char *file, int sec_tag, - uint8_t pdn_id, size_t fragment_size) +int fota_download_start(const char *host, const char *file, int sec_tag, uint8_t pdn_id, + size_t fragment_size) { int sec_tag_list[1] = { sec_tag }; uint8_t sec_tag_count = sec_tag < 0 ? 0 : 1; @@ -662,28 +654,28 @@ int fota_download_start_with_image_type(const char *host, const char *file, int sec_tag_list[1] = { sec_tag }; uint8_t sec_tag_count = sec_tag < 0 ? 0 : 1; - return fota_download(host, file, sec_tag_list, sec_tag_count, pdn_id, - fragment_size, expected_type); + return fota_download(host, file, sec_tag_list, sec_tag_count, pdn_id, fragment_size, + expected_type); } static int fota_download_object_init(void) { int err; - k_work_init_delayable(&dlc_with_offset_work, download_with_offset); - - err = download_client_init(&dlc, download_client_callback); - if (err != 0) { - return err; - } - #ifdef CONFIG_FOTA_DOWNLOAD_NATIVE_TLS /* Enable native TLS for the download client socket * if configured. */ - dlc.set_native_tls = true; + dl_host_cfg.native_tls = CONFIG_FOTA_DOWNLOAD_NATIVE_TLS; #endif + k_work_init_delayable(&dl_with_offset_work, download_with_offset); + + err = downloader_init(&dl, &dl_cfg); + if (err != 0) { + return err; + } + initialized = true; return 0; } @@ -750,9 +742,9 @@ int fota_download_cancel(void) atomic_set_bit(&flags, FLAG_CANCEL); - err = disconnect(); + err = dl_cancel(); if (err) { - LOG_ERR("%s failed to disconnect: %d", __func__, err); + LOG_ERR("%s failed to stop download: %d", __func__, err); return err; } diff --git a/subsys/net/lib/fota_download/src/util/fota_download_delta_modem.c b/subsys/net/lib/fota_download/src/util/fota_download_delta_modem.c index efadf6b4a95f..c70a7dfd03e0 100644 --- a/subsys/net/lib/fota_download/src/util/fota_download_delta_modem.c +++ b/subsys/net/lib/fota_download/src/util/fota_download_delta_modem.c @@ -13,8 +13,7 @@ LOG_MODULE_REGISTER(fota_download_delta_modem, CONFIG_LOG_DEFAULT_LEVEL); /* Initialized to value different than success (0) */ static int dfu_result = -1; -NRF_MODEM_LIB_ON_DFU_RES(fota_delta_modem_dfu_res_hook, - on_modem_dfu_res, NULL); +NRF_MODEM_LIB_ON_DFU_RES(fota_delta_modem_dfu_res_hook, on_modem_dfu_res, NULL); static void on_modem_dfu_res(int dfu_res, void *ctx) { diff --git a/subsys/net/lib/fota_download/src/util/fota_download_full_modem.c b/subsys/net/lib/fota_download/src/util/fota_download_full_modem.c index 390bd45ff36f..fb363f965efe 100644 --- a/subsys/net/lib/fota_download/src/util/fota_download_full_modem.c +++ b/subsys/net/lib/fota_download/src/util/fota_download_full_modem.c @@ -70,7 +70,11 @@ int fota_download_full_modem_stream_params_init(void) const struct dfu_target_full_modem_params params = { .buf = fota_download_fulmodem_buf, .len = sizeof(fota_download_fulmodem_buf), - .dev = &(struct dfu_target_fmfu_fdev){ .dev = flash_dev, .offset = 0, .size = 0 } + .dev = &(struct dfu_target_fmfu_fdev){ + .dev = flash_dev, + .offset = 0, + .size = 0 + } }; ret = dfu_target_full_modem_cfg(¶ms); diff --git a/subsys/net/lib/fota_download/src/util/fota_download_util.c b/subsys/net/lib/fota_download/src/util/fota_download_util.c index 824af65e4f55..2a09bf135869 100644 --- a/subsys/net/lib/fota_download/src/util/fota_download_util.c +++ b/subsys/net/lib/fota_download/src/util/fota_download_util.c @@ -32,14 +32,12 @@ #include "fota_download_smp.h" #endif -#include "download_client_internal.h" - LOG_MODULE_REGISTER(fota_download_util, CONFIG_FOTA_DOWNLOAD_LOG_LEVEL); /** * @brief FOTA download url data. */ -struct fota_download_client_url_data { +struct fota_download_url_data { /** Host name */ const char *host; /** Host name length */ @@ -62,7 +60,7 @@ static bool download_active; static enum dfu_target_image_type active_dfu_type; int fota_download_parse_dual_resource_locator(char *const file, bool s0_active, - const char **selected_path) + const char **selected_path) { if (file == NULL || selected_path == NULL) { LOG_ERR("Got NULL pointer"); @@ -119,10 +117,10 @@ static void fota_download_callback(const struct fota_download_evt *evt) } } -static int fota_download_client_url_parse(const char *uri, - struct fota_download_client_url_data *parsed_uri) +static int fota_download_url_parse(const char *uri, + struct fota_download_url_data *parsed_uri) { - int len, err, proto, type; + int len; char *e, *s; len = strlen(uri); @@ -136,12 +134,6 @@ static int fota_download_client_url_parse(const char *uri, } s += strlen("://"); - /* Verify that download client knows the protocol */ - err = url_parse_proto(uri, &proto, &type); - if (err) { - return err; - } - /* Find the end of host name, which is start of path */ e = strchr(s, '/'); @@ -163,11 +155,11 @@ static int fota_download_client_url_parse(const char *uri, static int download_url_parse(const char *uri, int sec_tag) { int ret; - struct fota_download_client_url_data parsed_uri; + struct fota_download_url_data parsed_uri; LOG_INF("Download url %s", uri); - ret = fota_download_client_url_parse(uri, &parsed_uri); + ret = fota_download_url_parse(uri, &parsed_uri); if (ret) { return ret; } @@ -237,8 +229,8 @@ int fota_download_util_stream_init(void) } int fota_download_util_download_start(const char *download_uri, - enum dfu_target_image_type dfu_target_type, int sec_tag, - fota_download_callback_t client_callback) + enum dfu_target_image_type dfu_target_type, int sec_tag, + fota_download_callback_t client_callback) { int ret; diff --git a/subsys/net/lib/lwm2m_client_utils/lwm2m/lwm2m_firmware.c b/subsys/net/lib/lwm2m_client_utils/lwm2m/lwm2m_firmware.c index 9018829c66f1..880d81377f54 100644 --- a/subsys/net/lib/lwm2m_client_utils/lwm2m/lwm2m_firmware.c +++ b/subsys/net/lib/lwm2m_client_utils/lwm2m/lwm2m_firmware.c @@ -72,7 +72,7 @@ static struct k_thread thread; /** Ensure that thread is ready for download */ static K_SEM_DEFINE(update_mutex, 0, 1); /* Internal thread stack. */ -static K_THREAD_STACK_MEMBER(thread_stack, +static K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_LWM2M_CLIENT_UTILS_FIRMWARE_UPDATE_THREAD_STACK_SIZE); #endif diff --git a/subsys/net/lib/mcumgr_smp_client/Kconfig b/subsys/net/lib/mcumgr_smp_client/Kconfig index 9e35c72a62a1..80f07babeb5a 100644 --- a/subsys/net/lib/mcumgr_smp_client/Kconfig +++ b/subsys/net/lib/mcumgr_smp_client/Kconfig @@ -9,7 +9,7 @@ menuconfig NRF_MCUMGR_SMP_CLIENT select BASE64 select DFU_TARGET select DFU_TARGET_SMP - select DOWNLOAD_CLIENT + select DOWNLOADER select FOTA_DOWNLOAD select NET_BUF select ZCBOR diff --git a/subsys/net/lib/mcumgr_smp_client/src/mcumgr_smp_client_shell.c b/subsys/net/lib/mcumgr_smp_client/src/mcumgr_smp_client_shell.c index 85ba442bb5a5..d1fa7684f56b 100644 --- a/subsys/net/lib/mcumgr_smp_client/src/mcumgr_smp_client_shell.c +++ b/subsys/net/lib/mcumgr_smp_client/src/mcumgr_smp_client_shell.c @@ -56,7 +56,7 @@ static void fota_download_shell_callback(const struct fota_download_evt *evt) break; case FOTA_DOWNLOAD_EVT_FINISHED: - LOG_INF("FOTA download finished"); + LOG_INF("FOTA downloader finished"); active_download = false; break; } diff --git a/subsys/net/lib/nrf_cloud/Kconfig.nrf_cloud_fota b/subsys/net/lib/nrf_cloud/Kconfig.nrf_cloud_fota index 4a2d47c7a5bd..95a9c8779f9c 100644 --- a/subsys/net/lib/nrf_cloud/Kconfig.nrf_cloud_fota +++ b/subsys/net/lib/nrf_cloud/Kconfig.nrf_cloud_fota @@ -9,7 +9,7 @@ menuconfig NRF_CLOUD_FOTA select FOTA_DOWNLOAD select FOTA_DOWNLOAD_PROGRESS_EVT select DFU_TARGET - select DOWNLOAD_CLIENT + select DOWNLOADER select REBOOT select CJSON_LIB select SETTINGS diff --git a/subsys/net/lib/nrf_cloud/Kconfig.nrf_cloud_pgps b/subsys/net/lib/nrf_cloud/Kconfig.nrf_cloud_pgps index 7d3d68762fbb..78f712fa7ce3 100644 --- a/subsys/net/lib/nrf_cloud/Kconfig.nrf_cloud_pgps +++ b/subsys/net/lib/nrf_cloud/Kconfig.nrf_cloud_pgps @@ -8,7 +8,7 @@ menuconfig NRF_CLOUD_PGPS depends on MODEM_INFO depends on MODEM_INFO_ADD_NETWORK depends on DATE_TIME - imply DOWNLOAD_CLIENT + imply DOWNLOADER select STREAM_FLASH_ERASE select SETTINGS select CJSON_LIB @@ -111,7 +111,7 @@ config NRF_CLOUD_PGPS_DOWNLOAD_TRANSPORT_HTTP bool "Transport P-GPS prediction data over HTTP(S)" help Enabling this option will make the nRF Cloud P-GPS library use the - download_client library to download prediction data. + downloader library to download prediction data. config NRF_CLOUD_PGPS_DOWNLOAD_TRANSPORT_CUSTOM bool "Transport P-GPS data using application-supplied transport" diff --git a/subsys/net/lib/nrf_cloud/include/nrf_cloud_download.h b/subsys/net/lib/nrf_cloud/include/nrf_cloud_download.h index 8115e5ad6064..285f170ad3da 100644 --- a/subsys/net/lib/nrf_cloud/include/nrf_cloud_download.h +++ b/subsys/net/lib/nrf_cloud/include/nrf_cloud_download.h @@ -8,7 +8,7 @@ #define NRF_CLOUD_DOWNLOAD_H__ #include -#include +#include #ifdef __cplusplus extern "C" { @@ -19,7 +19,7 @@ enum nrf_cloud_download_type { /* Download a FOTA update using the fota_download library */ NRF_CLOUD_DL_TYPE_FOTA, - /* Download data using the download_client library */ + /* Download data using the downloader library */ NRF_CLOUD_DL_TYPE_DL_CLIENT, NRF_CLOUD_DL_TYPE_DL__LAST @@ -42,14 +42,14 @@ struct nrf_cloud_download_data { /* File download path */ const char *path; - /* Download client configuration */ - struct download_client_cfg dl_cfg; + /* Downloader host configuration */ + struct downloader_host_cfg dl_host_conf; union { /* FOTA type data */ struct nrf_cloud_download_fota fota; - /* Download client type data */ - struct download_client *dlc; + /* Downloader data */ + struct downloader *dl; }; #if defined(CONFIG_NRF_CLOUD_COAP_DOWNLOADS) @@ -64,7 +64,7 @@ struct nrf_cloud_download_data { * If a FOTA download is started while a non-FOTA download is active, the non-FOTA * download is stopped. */ -int nrf_cloud_download_start(struct nrf_cloud_download_data *const dl); +int nrf_cloud_download_start(struct nrf_cloud_download_data *const cloud_dl); /** @brief Cancel the active download. * Call to stop the current download and reset the download state. diff --git a/subsys/net/lib/nrf_cloud/include/nrf_cloud_pgps_utils.h b/subsys/net/lib/nrf_cloud/include/nrf_cloud_pgps_utils.h index 74920b73bf8d..38adc879b9da 100644 --- a/subsys/net/lib/nrf_cloud/include/nrf_cloud_pgps_utils.h +++ b/subsys/net/lib/nrf_cloud/include/nrf_cloud_pgps_utils.h @@ -84,7 +84,6 @@ int npgps_download_init(npgps_buffer_handler_t buf_handler, npgps_eot_handler_t int npgps_download_start(const char *host, const char *file, int sec_tag, uint8_t pdn_id, size_t fragment_size); - #ifdef __cplusplus } #endif diff --git a/subsys/net/lib/nrf_cloud/src/nrf_cloud_codec_internal.c b/subsys/net/lib/nrf_cloud/src/nrf_cloud_codec_internal.c index f5fff7523632..3dcc48f878f1 100644 --- a/subsys/net/lib/nrf_cloud/src/nrf_cloud_codec_internal.c +++ b/subsys/net/lib/nrf_cloud/src/nrf_cloud_codec_internal.c @@ -2012,7 +2012,6 @@ int nrf_cloud_fota_job_decode(struct nrf_cloud_fota_job_info *const job_info, job_info->host = NULL; nrf_cloud_free(job_info->path); job_info->path = NULL; - job_info->type = NRF_CLOUD_FOTA_TYPE__INVALID; } diff --git a/subsys/net/lib/nrf_cloud/src/nrf_cloud_download.c b/subsys/net/lib/nrf_cloud/src/nrf_cloud_download.c index 35e34c350714..5a66f106dc91 100644 --- a/subsys/net/lib/nrf_cloud/src/nrf_cloud_download.c +++ b/subsys/net/lib/nrf_cloud/src/nrf_cloud_download.c @@ -70,7 +70,7 @@ static int coap_dl_connect_and_auth(void) return 0; } -static int fota_dl_evt_send(const struct download_client_evt *evt) +static int fota_dl_evt_send(const struct downloader_evt *evt) { #if defined(CONFIG_FOTA_DOWNLOAD_EXTERNAL_DL) return fota_download_external_evt_handle(evt); @@ -79,13 +79,13 @@ static int fota_dl_evt_send(const struct download_client_evt *evt) } static int coap_dl_event_send(struct nrf_cloud_download_data const *const dl, - const struct download_client_evt *const evt) + const struct downloader_evt *const evt) { /* Send events as if we are the downoad_client */ if (dl->type == NRF_CLOUD_DL_TYPE_FOTA) { return fota_dl_evt_send(evt); } else if (dl->type == NRF_CLOUD_DL_TYPE_DL_CLIENT) { - return dl->dlc->callback(evt); + return dl->dl->cfg.callback(evt); } return -EINVAL; @@ -104,13 +104,13 @@ static void coap_dl_cb(int16_t result_code, size_t offset, const uint8_t *payloa bool send_done_evt = last_block; bool stop_on_err = false; struct nrf_cloud_download_data *dl = (struct nrf_cloud_download_data *)user_data; - struct download_client_evt evt = {0}; + struct downloader_evt evt = {0}; LOG_DBG("CoAP result: %d, offset: 0x%X, len: 0x%X, last_block: %d", result_code, offset, len, last_block); if (result_code == COAP_RESPONSE_CODE_CONTENT) { - evt.id = DOWNLOAD_CLIENT_EVT_FRAGMENT; + evt.id = DOWNLOADER_EVT_FRAGMENT; evt.fragment.buf = payload; evt.fragment.len = len; } else if (result_code == -ECANCELED) { @@ -118,14 +118,14 @@ static void coap_dl_cb(int16_t result_code, size_t offset, const uint8_t *payloa /* This is not actually an error, just use the error event to indicate that * the transfer has been canceled */ - evt.id = DOWNLOAD_CLIENT_EVT_ERROR; + evt.id = DOWNLOADER_EVT_ERROR; evt.error = -ECANCELED; (void)coap_dl_event_send(dl, &evt); return; } else if (result_code != COAP_RESPONSE_CODE_OK) { LOG_ERR("Unexpected CoAP result: %d", result_code); LOG_DBG("CoAP response: %.*s", len, payload); - evt.id = DOWNLOAD_CLIENT_EVT_ERROR; + evt.id = DOWNLOADER_EVT_ERROR; /* Use -ECONNRESET to trigger retry mechanism used by fota_download and * the P-GPS download event handler */ @@ -135,7 +135,7 @@ static void coap_dl_cb(int16_t result_code, size_t offset, const uint8_t *payloa ret = coap_dl_event_send(dl, &evt); - if (evt.id == DOWNLOAD_CLIENT_EVT_FRAGMENT) { + if (evt.id == DOWNLOADER_EVT_FRAGMENT) { if (ret == 0) { /* Fragment was successfully processed */ dl->coap_rcvd_bytes += len; @@ -164,7 +164,7 @@ static void coap_dl_cb(int16_t result_code, size_t offset, const uint8_t *payloa LOG_INF("Download complete"); memset(&evt, 0, sizeof(evt)); - evt.id = DOWNLOAD_CLIENT_EVT_DONE; + evt.id = DOWNLOADER_EVT_DONE; ret = coap_dl_event_send(dl, &evt); if (ret) { @@ -186,7 +186,7 @@ static void coap_dl_cb(int16_t result_code, size_t offset, const uint8_t *payloa if (send_closed_evt) { memset(&evt, 0, sizeof(evt)); - evt.id = DOWNLOAD_CLIENT_EVT_CLOSED; + evt.id = DOWNLOADER_EVT_STOPPED; (void)coap_dl_event_send(dl, &evt); } @@ -351,19 +351,19 @@ static void resume_work_fn(struct k_work *unused) /* On failure, send the events required to generate the error/done status */ if (ret) { - struct download_client_evt evt = {0}; + struct downloader_evt evt = {0}; LOG_ERR("Failed to resume CoAP download"); /* Send a non-recoverable error event (not ECONN) */ - evt.id = DOWNLOAD_CLIENT_EVT_ERROR; + evt.id = DOWNLOADER_EVT_ERROR; evt.error = -EIO; (void)coap_dl_event_send(&active_dl, &evt); /* Send a closed event to ensure the terminal fota_download event is generated */ if (active_dl.type == NRF_CLOUD_DL_TYPE_FOTA) { memset(&evt, 0, sizeof(evt)); - evt.id = DOWNLOAD_CLIENT_EVT_CLOSED; + evt.id = DOWNLOADER_EVT_STOPPED; (void)coap_dl_event_send(&active_dl, &evt); } } @@ -406,33 +406,33 @@ static int fota_start(struct nrf_cloud_download_data *const dl) #endif /* CONFIG_NRF_CLOUD_COAP_DOWNLOADS */ return fota_download_start_with_image_type(dl->host, dl->path, - dl->dl_cfg.sec_tag_count ? dl->dl_cfg.sec_tag_list[0] : -1, - dl->dl_cfg.pdn_id, dl->dl_cfg.frag_size_override, dl->fota.expected_type); + dl->dl_host_conf.sec_tag_count ? dl->dl_host_conf.sec_tag_list[0] : -1, + dl->dl_host_conf.pdn_id, dl->dl_host_conf.range_override, dl->fota.expected_type); #endif /* CONFIG_FOTA_DOWNLOAD */ return -ENOTSUP; } -static int dlc_start(struct nrf_cloud_download_data *const dl) +static int dl_start(struct nrf_cloud_download_data *const cloud_dl) { - __ASSERT(dl->dlc != NULL, "Download client is NULL"); - __ASSERT(dl->dlc->callback != NULL, "Download client callback is NULL"); + __ASSERT(cloud_dl->dl != NULL, "Download client is NULL"); #if defined(CONFIG_NRF_CLOUD_COAP_DOWNLOADS) - return coap_dl(dl); + return coap_dl(cloud_dl); #endif /* CONFIG_NRF_CLOUD_COAP_DOWNLOADS */ - return download_client_get(dl->dlc, dl->host, &dl->dl_cfg, dl->path, 0); + return downloader_get_with_host_and_file(cloud_dl->dl, &cloud_dl->dl_host_conf, + cloud_dl->host, cloud_dl->path, 0); } -static int dlc_disconnect(struct nrf_cloud_download_data *const dl) +static int dl_disconnect(struct nrf_cloud_download_data *const dl) { #if defined(CONFIG_NRF_CLOUD_COAP_DOWNLOADS) return coap_dl_disconnect(); #endif /* CONFIG_NRF_CLOUD_COAP_DOWNLOADS */ - return download_client_disconnect(dl->dlc); + return downloader_cancel(dl->dl); } static void active_dl_reset(void) @@ -464,7 +464,7 @@ void nrf_cloud_download_cancel(void) if (active_dl.type == NRF_CLOUD_DL_TYPE_FOTA) { ret = fota_dl_cancel(&active_dl); } else if (active_dl.type == NRF_CLOUD_DL_TYPE_DL_CLIENT) { - ret = dlc_disconnect(&active_dl); + ret = dl_disconnect(&active_dl); } else { LOG_WRN("No active download to cancel"); } @@ -497,39 +497,39 @@ static bool check_fota_file_path_len(char const *const file_path) return true; } -int nrf_cloud_download_start(struct nrf_cloud_download_data *const dl) +int nrf_cloud_download_start(struct nrf_cloud_download_data *const cloud_dl) { - if (!dl || !dl->path || (dl->type <= NRF_CLOUD_DL_TYPE_NONE) || - (dl->type >= NRF_CLOUD_DL_TYPE_DL__LAST)) { + int ret = 0; + + if (!cloud_dl || !cloud_dl->path || (cloud_dl->type <= NRF_CLOUD_DL_TYPE_NONE) || + (cloud_dl->type >= NRF_CLOUD_DL_TYPE_DL__LAST)) { return -EINVAL; } - if (dl->type == NRF_CLOUD_DL_TYPE_FOTA) { + if (cloud_dl->type == NRF_CLOUD_DL_TYPE_FOTA) { if (!IS_ENABLED(CONFIG_FOTA_DOWNLOAD)) { return -ENOTSUP; } - if (!dl->fota.cb) { + if (!cloud_dl->fota.cb) { return -ENOEXEC; } - if ((dl->fota.expected_type == DFU_TARGET_IMAGE_TYPE_SMP) && + if ((cloud_dl->fota.expected_type == DFU_TARGET_IMAGE_TYPE_SMP) && !IS_ENABLED(CONFIG_NRF_CLOUD_FOTA_SMP)) { return -ENOSYS; } } - if (!check_fota_file_path_len(dl->path)) { + if (!check_fota_file_path_len(cloud_dl->path)) { LOG_ERR("FOTA download file path is too long"); return -E2BIG; } - int ret = 0; - k_mutex_lock(&active_dl_mutex, K_FOREVER); /* FOTA has priority */ if ((active_dl.type == NRF_CLOUD_DL_TYPE_FOTA) || ((active_dl.type != NRF_CLOUD_DL_TYPE_NONE) && - (dl->type != NRF_CLOUD_DL_TYPE_FOTA))) { + (cloud_dl->type != NRF_CLOUD_DL_TYPE_FOTA))) { k_mutex_unlock(&active_dl_mutex); /* A download of equal or higher priority is already active. */ return -EBUSY; @@ -540,21 +540,21 @@ int nrf_cloud_download_start(struct nrf_cloud_download_data *const dl) */ if (active_dl.type == NRF_CLOUD_DL_TYPE_DL_CLIENT) { LOG_INF("Stopping active download, incoming FOTA update download has priority"); - ret = dlc_disconnect(&active_dl); + ret = dl_disconnect(&active_dl); if (ret) { LOG_ERR("Download disconnect failed, error %d", ret); } } - active_dl = *dl; + active_dl = *cloud_dl; if (active_dl.type == NRF_CLOUD_DL_TYPE_FOTA) { ret = fota_start(&active_dl); } else if (active_dl.type == NRF_CLOUD_DL_TYPE_DL_CLIENT) { - ret = dlc_start(&active_dl); + ret = dl_start(&active_dl); if (ret) { - (void)dlc_disconnect(&active_dl); + (void)dl_disconnect(&active_dl); } } else { LOG_WRN("Unhandled download type: %d", active_dl.type); diff --git a/subsys/net/lib/nrf_cloud/src/nrf_cloud_fota.c b/subsys/net/lib/nrf_cloud/src/nrf_cloud_fota.c index 8ad69005d5df..d150407b9b62 100644 --- a/subsys/net/lib/nrf_cloud/src/nrf_cloud_fota.c +++ b/subsys/net/lib/nrf_cloud/src/nrf_cloud_fota.c @@ -869,15 +869,15 @@ static int start_job(struct nrf_cloud_fota_job *const job, const bool send_evt) sec_tag = nrf_cloud_sec_tag_get(); - struct nrf_cloud_download_data dl = { + struct nrf_cloud_download_data cloud_dl = { .type = NRF_CLOUD_DL_TYPE_FOTA, .host = job->info.host, .path = job->info.path, - .dl_cfg = { + .dl_host_conf = { .sec_tag_list = &sec_tag, .sec_tag_count = (sec_tag < 0 ? 0 : 1), .pdn_id = 0, - .frag_size_override = CONFIG_NRF_CLOUD_FOTA_DOWNLOAD_FRAGMENT_SIZE, + .range_override = CONFIG_NRF_CLOUD_FOTA_DOWNLOAD_FRAGMENT_SIZE, }, .fota = { .expected_type = img_type, @@ -886,7 +886,7 @@ static int start_job(struct nrf_cloud_fota_job *const job, const bool send_evt) } }; - ret = nrf_cloud_download_start(&dl); + ret = nrf_cloud_download_start(&cloud_dl); if (ret) { LOG_ERR("Failed to start FOTA download: %d", ret); ret = -EPIPE; @@ -1118,11 +1118,10 @@ static int handle_mqtt_evt_publish(const struct mqtt_evt *evt) LOG_INF("Job %s already completed... skipping", last_job); nrf_cloud_fota_job_free(job_info); } else { - LOG_DBG("Job ID: %s, type: %d, size: %d", + LOG_DBG("Job ID: %s, type: %d, size: %d, file: %s/%s", job_info->id, job_info->type, - job_info->file_size); - LOG_DBG("File: %s/%s", + job_info->file_size, job_info->host, job_info->path); } diff --git a/subsys/net/lib/nrf_cloud/src/nrf_cloud_fota_poll.c b/subsys/net/lib/nrf_cloud/src/nrf_cloud_fota_poll.c index c506edb4fa07..0b893b0a833e 100644 --- a/subsys/net/lib/nrf_cloud/src/nrf_cloud_fota_poll.c +++ b/subsys/net/lib/nrf_cloud/src/nrf_cloud_fota_poll.c @@ -462,19 +462,20 @@ static int start_download(void) } LOG_INF("Starting FOTA download of %s/%s", job.host, job.path); - sec_tag = nrf_cloud_sec_tag_get(); - struct nrf_cloud_download_data dl = { + struct nrf_cloud_download_data cloud_dl = { .type = NRF_CLOUD_DL_TYPE_FOTA, .host = job.host, .path = job.path, - .dl_cfg = { + .dl_host_conf = { .sec_tag_list = &sec_tag, .sec_tag_count = (sec_tag < 0 ? 0 : 1), .pdn_id = 0, - .frag_size_override = ctx_ptr->fragment_size ? ctx_ptr->fragment_size : - CONFIG_NRF_CLOUD_FOTA_DOWNLOAD_FRAGMENT_SIZE, + .range_override = + ctx_ptr->fragment_size + ? ctx_ptr->fragment_size + : CONFIG_NRF_CLOUD_FOTA_DOWNLOAD_FRAGMENT_SIZE, }, .fota = { .expected_type = ctx_ptr->img_type, @@ -489,7 +490,7 @@ static int start_download(void) fota_status = NRF_CLOUD_FOTA_IN_PROGRESS; fota_status_details = NULL; - ret = nrf_cloud_download_start(&dl); + ret = nrf_cloud_download_start(&cloud_dl); if (ret) { LOG_ERR("Failed to start FOTA download, error: %d", ret); fota_status = NRF_CLOUD_FOTA_QUEUED; diff --git a/subsys/net/lib/nrf_cloud/src/nrf_cloud_pgps.c b/subsys/net/lib/nrf_cloud/src/nrf_cloud_pgps.c index 10d49943e9dc..56fc8344c56f 100644 --- a/subsys/net/lib/nrf_cloud/src/nrf_cloud_pgps.c +++ b/subsys/net/lib/nrf_cloud/src/nrf_cloud_pgps.c @@ -864,9 +864,10 @@ static int pgps_request_all(void) /* handle incoming P-GPS response packets */ int nrf_cloud_pgps_process(const char *buf, size_t buf_len) { - static char host[CONFIG_DOWNLOAD_CLIENT_MAX_HOSTNAME_SIZE]; - static char path[CONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE]; int err; + static char host[CONFIG_DOWNLOADER_MAX_HOSTNAME_SIZE]; + static char path[CONFIG_DOWNLOADER_MAX_FILENAME_SIZE]; + struct nrf_cloud_pgps_result pgps_dl = { .host = host, .host_sz = sizeof(host), @@ -921,11 +922,13 @@ int nrf_cloud_pgps_update(struct nrf_cloud_pgps_result *file_location) memmove(&file_location->host[4], &file_location->host[5], strlen(&file_location->host[4])); + sec_tag = -1; } err = npgps_download_start(file_location->host, file_location->path, sec_tag, 0, FRAGMENT_SIZE); + if (err) { state = PGPS_REQUEST_NEEDED; /* Will try again next time. */ } diff --git a/subsys/net/lib/nrf_cloud/src/nrf_cloud_pgps_utils.c b/subsys/net/lib/nrf_cloud/src/nrf_cloud_pgps_utils.c index eab4e8cd83dc..6753c595d063 100644 --- a/subsys/net/lib/nrf_cloud/src/nrf_cloud_pgps_utils.c +++ b/subsys/net/lib/nrf_cloud/src/nrf_cloud_pgps_utils.c @@ -47,13 +47,20 @@ static struct nrf_cloud_pgps_header saved_header; static K_SEM_DEFINE(dl_active, 1, 1); -static struct download_client dlc; +static char dl_buf[2048]; +static struct downloader dl; +static int downloader_callback(const struct downloader_evt *event); +static struct downloader_cfg dl_cfg = { + .callback = downloader_callback, + .buf = dl_buf, + .buf_size = sizeof(dl_buf), +}; + static int sec_tag_list[1]; static int socket_retries_left; static npgps_buffer_handler_t buffer_handler; static npgps_eot_handler_t eot_handler; -static int download_client_callback(const struct download_client_evt *event); static int settings_set(const char *key, size_t len_rd, settings_read_cb read_cb, void *cb_arg); @@ -473,15 +480,13 @@ int npgps_download_init(npgps_buffer_handler_t buf_handler, npgps_eot_handler_t buffer_handler = buf_handler; eot_handler = end_handler; - return download_client_init(&dlc, download_client_callback); + return downloader_init(&dl, &dl_cfg); } int npgps_download_start(const char *host, const char *file, int sec_tag, uint8_t pdn_id, size_t fragment_size) { - if (host == NULL || file == NULL) { - return -EINVAL; - } + int err; #if defined(CONFIG_NET_IPV6) && defined(CONFIG_NET_IPV4) int family = AF_UNSPEC; @@ -492,32 +497,34 @@ int npgps_download_start(const char *host, const char *file, int sec_tag, #else int family = AF_UNSPEC; #endif /* defined(CONFIG_NET_IPV6) && defined(CONFIG_NET_IPV4) */ - int err; - struct nrf_cloud_download_data dl = { + + if (host == NULL || file == NULL) { + return -EINVAL; + } + + struct nrf_cloud_download_data cloud_dl = { .type = NRF_CLOUD_DL_TYPE_DL_CLIENT, .host = host, .path = file, - .dl_cfg = { + .dl_host_conf = { .sec_tag_count = 0, .sec_tag_list = NULL, .pdn_id = pdn_id, - .frag_size_override = fragment_size, - .set_tls_hostname = false, - .family = family + .range_override = fragment_size, + .family = family, }, - .dlc = &dlc + .dl = &dl, }; if (sec_tag != -1) { sec_tag_list[0] = sec_tag; - dl.dl_cfg.sec_tag_list = sec_tag_list; - dl.dl_cfg.sec_tag_count = 1; - dl.dl_cfg.set_tls_hostname = true; + cloud_dl.dl_host_conf.sec_tag_list = sec_tag_list; + cloud_dl.dl_host_conf.sec_tag_count = 1; } socket_retries_left = SOCKET_RETRIES; - err = nrf_cloud_download_start(&dl); + err = nrf_cloud_download_start(&cloud_dl); if (err) { LOG_ERR("Failed to start P-GPS download, error: %d", err); eot_handler(err); /* Let requester know so it can clean up. */ @@ -526,7 +533,7 @@ int npgps_download_start(const char *host, const char *file, int sec_tag, return err; } -static int download_client_callback(const struct download_client_evt *event) +static int downloader_callback(const struct downloader_evt *event) { int err = 0; @@ -535,17 +542,17 @@ static int download_client_callback(const struct download_client_evt *event) } switch (event->id) { - case DOWNLOAD_CLIENT_EVT_FRAGMENT: + case DOWNLOADER_EVT_FRAGMENT: err = buffer_handler((uint8_t *)event->fragment.buf, event->fragment.len); if (!err) { return 0; } break; - case DOWNLOAD_CLIENT_EVT_DONE: + case DOWNLOADER_EVT_DONE: LOG_DBG("Download client done"); break; - case DOWNLOAD_CLIENT_EVT_ERROR: { + case DOWNLOADER_EVT_ERROR: { if ((event->error == -ECANCELED) && IS_ENABLED(CONFIG_NRF_CLOUD_COAP_DOWNLOADS)) { eot_handler(event->error); return 0; @@ -568,9 +575,11 @@ static int download_client_callback(const struct download_client_evt *event) return 0; } - /* CoAP downloads do not need to disconnect since they don't directly use download_client */ + /* CoAP downloads do not need to disconnect since they don't directly use downloader */ #if !defined(CONFIG_NRF_CLOUD_COAP_DOWNLOADS) - int ret = download_client_disconnect(&dlc); + int ret; + + ret = downloader_cancel(&dl); if (ret) { LOG_ERR("Error disconnecting from download client:%d", ret); diff --git a/tests/subsys/net/lib/aws_fota/aws_fota_json/CMakeLists.txt b/tests/subsys/net/lib/aws_fota/aws_fota_json/CMakeLists.txt index 04f281620220..2aaa8852fdc7 100644 --- a/tests/subsys/net/lib/aws_fota/aws_fota_json/CMakeLists.txt +++ b/tests/subsys/net/lib/aws_fota/aws_fota_json/CMakeLists.txt @@ -30,6 +30,6 @@ target_include_directories(app # is not executed. Hence these can not be set through prj.conf. target_compile_options(app PRIVATE - -DCONFIG_DOWNLOAD_CLIENT_MAX_HOSTNAME_SIZE=1024 - -DCONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE=1024 + -DCONFIG_DOWNLOADER_MAX_HOSTNAME_SIZE=1024 + -DCONFIG_DOWNLOADER_MAX_FILENAME_SIZE=1024 ) diff --git a/tests/subsys/net/lib/downloader/CMakeLists.txt b/tests/subsys/net/lib/downloader/CMakeLists.txt new file mode 100644 index 000000000000..45a2aa1f4982 --- /dev/null +++ b/tests/subsys/net/lib/downloader/CMakeLists.txt @@ -0,0 +1,48 @@ +# +# Copyright (c) 2024 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(downloader) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) + +test_runner_generate(src/main.c) + +target_sources(app + PRIVATE + ${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/downloader/src/downloader.c + ${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/downloader/src/dl_socket.c + ${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/downloader/src/dl_parse.c + ${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/downloader/src/sanity.c + ${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/downloader/src/transports/coap.c + ${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/downloader/src/transports/http.c +) + +zephyr_include_directories(${ZEPHYR_NRF_MODULE_DIR}/include/net/) +zephyr_include_directories(${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/downloader/include/) +zephyr_include_directories(${ZEPHYR_BASE}/subsys/net/ip/) +zephyr_include_directories(${ZEPHYR_BASE}/subsys/net/lib/sockets) +zephyr_include_directories(${ZEPHYR_BASE}/subsys/testsuite/include) + +zephyr_linker_sources(RODATA ${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/downloader/dl_transports.ld) + +target_compile_options(app + PRIVATE + -DCONFIG_DOWNLOADER_MAX_HOSTNAME_SIZE=256 + -DCONFIG_DOWNLOADER_MAX_FILENAME_SIZE=256 + -DCONFIG_DOWNLOADER_TRANSPORT_PARAMS_SIZE=128 + -DCONFIG_DOWNLOADER_STACK_SIZE=2048 + -DCONFIG_NET_SOCKETS_POSIX_NAMES=y + -DCONFIG_NET_IPV6=y + -DCONFIG_NET_IPV4=y + -DCONFIG_COAP_MAX_RETRANSMIT=2 + -DCONFIG_COAP_INIT_ACK_TIMEOUT_MS=100 + -DCONFIG_COAP_BACKOFF_PERCENT=5 + -DCONFIG_COAP_BLOCK_SIZE=5 +) diff --git a/tests/subsys/net/lib/downloader/prj.conf b/tests/subsys/net/lib/downloader/prj.conf new file mode 100644 index 000000000000..845e0c0a40b5 --- /dev/null +++ b/tests/subsys/net/lib/downloader/prj.conf @@ -0,0 +1,7 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# +CONFIG_UNITY=y +CONFIG_PIPES=y diff --git a/tests/subsys/net/lib/downloader/src/main.c b/tests/subsys/net/lib/downloader/src/main.c new file mode 100644 index 000000000000..6342d1226545 --- /dev/null +++ b/tests/subsys/net/lib/downloader/src/main.c @@ -0,0 +1,2167 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#define HOSTNAME "server.com" +#define HOSTNAME2 "server2.com" + +#define NO_PROTO_URL "server.com/path/to/file.end" +#define HTTP_URL "http://server.com:80/path/to/file.end" +#define HTTP_URL_FILE2 "http://server.com/path/to/file2.end" +#define HTTP_URL_HOST2 "http://server2.com/path/to/file.end" +#define HTTPS_URL "https://server.com/path/to/file.end" +#define COAP_URL "coap://server.com/path/to/file.end" +#define COAPS_URL "coaps://server.com/path/to/file.end" +#define BAD_URL "bad://server.com/path/to/file.end" +#define BAD_URL2 "bad:/server.com:80/path/to/file.end" +#define BAD_URL_NO_FILE "http://server.com" + +#define HTTP_HOST "http://server.com" +#define HTTPS_HOST "https://server.com" +#define COAP_HOST "coap://server.com" +#define COAPS_HOST "coaps://server.com" +#define FILE_PATH "path/to/file.end" + +#define FILE_PATH_LONG "this/path/is/too/long/to/fit/in/the/buffer/provided/to/the/downloader/" \ + "for/download_get_with_file_and_hostname" + +#define HTTP_HDR_OK "HTTP/1.1 200 OK\r\n" \ +"Accept-Ranges: bytes\r\n" \ +"Age: 497805\r\n" \ +"Cache-Control: max-age=604800\r\n" \ +"Content-Encoding: gzip\r\n" \ +"Content-Length: 128\r\n" \ +"Content-Type: text/html; charset=UTF-8\r\n" \ +"Date: Wed, 06 Nov 2024 13:00:48 GMT\r\n" \ +"Etag: \"3147526947\"\r\n" \ +"Expires: Wed, 23 Nov 2124 23:12:95 GMT\r\n" \ +"Last-Modified: Thu, 06 Nov 2024 14:17:23 GMT\r\n" \ +"Server: ECAcc (nyd/D184)\r\n" \ +"Vary: Accept-Encoding\r\n" \ +"X-Cache: HIT\r\n\r\n" + +#define HTTP_HDR_OK_WITH_PAYLOAD "HTTP/1.1 200 OK\r\n" \ +"Accept-Ranges: bytes\r\n" \ +"Age: 497805\r\n" \ +"Cache-Control: max-age=604800\r\n" \ +"Content-Encoding: gzip\r\n" \ +"Content-Length: 20\r\n" \ +"Content-Type: text/html; charset=UTF-8\r\n" \ +"Date: Wed, 06 Nov 2024 13:00:48 GMT\r\n" \ +"Etag: \"3147526947\"\r\n" \ +"Expires: Wed, 23 Nov 2124 23:12:95 GMT\r\n" \ +"Last-Modified: Thu, 06 Nov 2024 14:17:23 GMT\r\n" \ +"Server: ECAcc (nyd/D184)\r\n" \ +"Vary: Accept-Encoding\r\n" \ +"X-Cache: HIT\r\n\r\n"\ +"This is the payload!" + +#define HTTP_HDR_OK_PROGRESS "HTTP/1.1 206 OK\r\n" \ +"Accept-Ranges: bytes\r\n" \ +"Age: 497805\r\n" \ +"Cache-Control: max-age=604800\r\n" \ +"Content-Encoding: gzip\r\n" \ +"Content-Length: 128\r\n" \ +"Content-Type: text/html; charset=UTF-8\r\n" \ +"Date: Wed, 06 Nov 2024 13:00:48 GMT\r\n" \ +"Etag: \"3147526947\"\r\n" \ +"Expires: Wed, 23 Nov 2124 23:12:95 GMT\r\n" \ +"Last-Modified: Thu, 06 Nov 2024 14:17:23 GMT\r\n" \ +"Server: ECAcc (nyd/D184)\r\n" \ +"Vary: Accept-Encoding\r\n" \ +"X-Cache: HIT\r\n\r\n" + +#define PAYLOAD "This is the payload!" + +#define FD 0 + +static int dl_callback(const struct downloader_evt *event); +static int dl_callback_abort(const struct downloader_evt *event); + +static struct downloader dl; + +char dl_buf[2048]; +struct downloader_cfg dl_cfg = { + .callback = dl_callback, + .buf = dl_buf, + .buf_size = sizeof(dl_buf), +}; + +struct downloader_cfg dl_cfg_small_buffer = { + .callback = dl_callback, + .buf = dl_buf, + .buf_size = 32, +}; + +struct downloader_cfg dl_cfg_cb_abort = { + .callback = dl_callback_abort, + .buf = dl_buf, + .buf_size = sizeof(dl_buf), +}; + +static struct downloader_host_cfg dl_host_cfg = { + .pdn_id = 1, + .keep_connection = true, +}; + +int sec_tags[] = {1, 2, 3}; +static struct downloader_host_cfg dl_host_conf_w_sec_tags = { + .pdn_id = 1, + .sec_tag_list = sec_tags, + .sec_tag_count = ARRAY_SIZE(sec_tags), +}; + +static struct downloader_host_cfg dl_host_conf_w_sec_tags_and_cid = { + .pdn_id = 1, + .sec_tag_list = sec_tags, + .sec_tag_count = ARRAY_SIZE(sec_tags), + .cid = true, +}; + +DEFINE_FFF_GLOBALS; + +FAKE_VALUE_FUNC(int, z_impl_zsock_setsockopt, int, int, int, const void *, socklen_t); +FAKE_VALUE_FUNC(int, z_impl_zsock_socket, int, int, int); +FAKE_VALUE_FUNC(int, z_impl_zsock_connect, int, const struct sockaddr *, socklen_t); +FAKE_VALUE_FUNC(int, z_impl_zsock_close, int) +FAKE_VALUE_FUNC(ssize_t, z_impl_zsock_send, int, const void *, size_t, int) +FAKE_VALUE_FUNC(ssize_t, z_impl_zsock_recv, int, void *, size_t, int) +FAKE_VALUE_FUNC(int, zsock_getaddrinfo, const char *, const char *, const struct zsock_addrinfo *, + struct zsock_addrinfo **) +FAKE_VOID_FUNC(zsock_freeaddrinfo, struct zsock_addrinfo *); + +FAKE_VALUE_FUNC(int, z_impl_zsock_inet_pton, sa_family_t, const char *, void *) +FAKE_VALUE_FUNC(char *, z_impl_net_addr_ntop, sa_family_t, const void *, char *, size_t) +FAKE_VALUE_FUNC(ssize_t, z_impl_zsock_sendto, int, const void *, size_t, int, + const struct sockaddr *, socklen_t); +FAKE_VALUE_FUNC(ssize_t, z_impl_zsock_recvfrom, int, void *, size_t, int, struct sockaddr *, + socklen_t *); +FAKE_VOID_FUNC(z_impl_sys_rand_get, void *, size_t); + +FAKE_VALUE_FUNC(int, coap_get_option_int, const struct coap_packet *, uint16_t); +FAKE_VALUE_FUNC(int, coap_block_transfer_init, struct coap_block_context *, enum coap_block_size, + size_t); +FAKE_VOID_FUNC(coap_pending_clear, struct coap_pending *); +FAKE_VALUE_FUNC(bool, coap_pending_cycle, struct coap_pending *); +FAKE_VALUE_FUNC(int, coap_update_from_block, const struct coap_packet *, + struct coap_block_context *); +FAKE_VALUE_FUNC(size_t, coap_next_block, const struct coap_packet *, struct coap_block_context *); +FAKE_VALUE_FUNC(int, coap_packet_parse, struct coap_packet *, uint8_t *, uint16_t, + struct coap_option *, uint8_t); +FAKE_VALUE_FUNC(uint16_t, coap_header_get_id, const struct coap_packet *); +FAKE_VALUE_FUNC(uint8_t, coap_header_get_type, const struct coap_packet *); +FAKE_VALUE_FUNC(uint8_t, coap_header_get_code, const struct coap_packet *); +FAKE_VALUE_FUNC(const uint8_t *, coap_packet_get_payload, const struct coap_packet *, uint16_t *); +FAKE_VALUE_FUNC(int, coap_packet_init, struct coap_packet *, uint8_t *, uint16_t, uint8_t, uint8_t, + uint8_t, const uint8_t *, uint8_t, uint16_t); +FAKE_VALUE_FUNC(uint8_t *, coap_next_token); +FAKE_VALUE_FUNC(int, coap_packet_append_option, struct coap_packet *, uint16_t, const uint8_t *, + uint16_t); +FAKE_VALUE_FUNC(int, coap_append_block2_option, struct coap_packet *, struct coap_block_context *); +FAKE_VALUE_FUNC(int, coap_append_size2_option, struct coap_packet *, struct coap_block_context *); +FAKE_VALUE_FUNC(struct coap_transmission_parameters, coap_get_transmission_parameters); +FAKE_VALUE_FUNC(int, coap_pending_init, struct coap_pending *, const struct coap_packet *, + const struct sockaddr *, const struct coap_transmission_parameters *); + +uint16_t message_id; +uint16_t coap_next_id(void) +{ + return message_id++; +} + +static struct sockaddr server_sockaddr = { + .sa_family = AF_INET, +}; + +static struct zsock_addrinfo server_addrinfo = { + .ai_addr = &server_sockaddr, + .ai_addrlen = sizeof(struct sockaddr), +}; + +static struct sockaddr server_sockaddr6 = { + .sa_family = AF_INET6, +}; + +static struct zsock_addrinfo server_addrinfo6 = { + .ai_addr = &server_sockaddr6, + .ai_addrlen = sizeof(struct sockaddr), +}; + +int zsock_getaddrinfo_server_ok(const char *host, const char *service, + const struct zsock_addrinfo *hints, + struct zsock_addrinfo **res) +{ + TEST_ASSERT_EQUAL_STRING(HOSTNAME, host); + + if (hints->ai_family == AF_INET) { + *res = &server_addrinfo; + return 0; + } else if (hints->ai_family == AF_INET6) { + *res = &server_addrinfo6; + return 0; + } + + errno = ENOPROTOOPT; + return EAI_SYSTEM; +} + +int zsock_getaddrinfo_server2_ok(const char *host, const char *service, + const struct zsock_addrinfo *hints, + struct zsock_addrinfo **res) +{ + TEST_ASSERT_EQUAL_STRING(HOSTNAME2, host); + + if (hints->ai_family == AF_INET) { + *res = &server_addrinfo; + return 0; + } else if (hints->ai_family == AF_INET6) { + *res = &server_addrinfo6; + return 0; + } + + errno = ENOPROTOOPT; + return EAI_SYSTEM; +} + + +int zsock_getaddrinfo_server_ipv6_fail_ipv4_ok(const char *host, const char *service, + const struct zsock_addrinfo *hints, + struct zsock_addrinfo **res) +{ + if (hints->ai_family == AF_INET6) { + /* Fail on IPv6 to retry IPv4 */ + errno = ENOPROTOOPT; + return EAI_SYSTEM; + } + + TEST_ASSERT_EQUAL_STRING(HOSTNAME, host); + TEST_ASSERT_EQUAL(AF_INET, hints->ai_family); + *res = &server_addrinfo; + + return 0; +} + +int zsock_getaddrinfo_server_enetunreach(const char *host, const char *service, + const struct zsock_addrinfo *hints, + struct zsock_addrinfo **res) +{ + errno = ENETUNREACH; + return EAI_SYSTEM; +} + +void zsock_freeaddrinfo_server_ipv4(struct zsock_addrinfo *addr) +{ + TEST_ASSERT_EQUAL_PTR(&server_addrinfo, addr); +} + +void zsock_freeaddrinfo_server_ipv6(struct zsock_addrinfo *addr) +{ + TEST_ASSERT_EQUAL_PTR(&server_addrinfo6, addr); +} + +void zsock_freeaddrinfo_server_ipv6_then_ipv4(struct zsock_addrinfo *addr) +{ + switch (zsock_freeaddrinfo_fake.call_count) { + case 1: + TEST_ASSERT_EQUAL_PTR(&server_addrinfo6, addr); + break; + case 2: + TEST_ASSERT_EQUAL_PTR(&server_addrinfo, addr); + default: + + } + +} + +int z_impl_zsock_socket_http_ipv4_ok(int family, int type, int proto) +{ + TEST_ASSERT_EQUAL(AF_INET, family); + TEST_ASSERT_EQUAL(SOCK_STREAM, type); + TEST_ASSERT_EQUAL(IPPROTO_TCP, proto); + + return FD; +} + +int z_impl_zsock_socket_http_ipv6_ok(int family, int type, int proto) +{ + TEST_ASSERT_EQUAL(AF_INET6, family); + TEST_ASSERT_EQUAL(SOCK_STREAM, type); + TEST_ASSERT_EQUAL(IPPROTO_TCP, proto); + + return FD; +} + +int z_impl_zsock_socket_http_ipv6_then_ipv4(int family, int type, int proto) +{ + switch (z_impl_zsock_socket_fake.call_count) { + case 1: + TEST_ASSERT_EQUAL(AF_INET6, family); + break; + case 2: + default: + TEST_ASSERT_EQUAL(AF_INET, family); + } + + TEST_ASSERT_EQUAL(SOCK_STREAM, type); + TEST_ASSERT_EQUAL(IPPROTO_TCP, proto); + + return FD; +} + +int z_impl_zsock_socket_https_ipv6_ok(int family, int type, int proto) +{ + TEST_ASSERT_EQUAL(AF_INET6, family); + TEST_ASSERT_EQUAL(SOCK_STREAM, type); + TEST_ASSERT_EQUAL(IPPROTO_TLS_1_2, proto); + + return FD; +} + +int z_impl_zsock_socket_coap_ipv4_ok(int family, int type, int proto) +{ + TEST_ASSERT_EQUAL(AF_INET, family); + TEST_ASSERT_EQUAL(SOCK_DGRAM, type); + TEST_ASSERT_EQUAL(IPPROTO_UDP, proto); + + return FD; +} + +int z_impl_zsock_socket_coap_ipv6_ok(int family, int type, int proto) +{ + TEST_ASSERT_EQUAL(AF_INET6, family); + TEST_ASSERT_EQUAL(SOCK_DGRAM, type); + TEST_ASSERT_EQUAL(IPPROTO_UDP, proto); + + return FD; +} + +int z_impl_zsock_socket_coaps_ipv4_ok(int family, int type, int proto) +{ + TEST_ASSERT_EQUAL(AF_INET, family); + TEST_ASSERT_EQUAL(SOCK_DGRAM, type); + TEST_ASSERT_EQUAL(IPPROTO_DTLS_1_2, proto); + + return FD; +} + +int z_impl_zsock_socket_coaps_ipv6_ok(int family, int type, int proto) +{ + TEST_ASSERT_EQUAL(AF_INET6, family); + TEST_ASSERT_EQUAL(SOCK_DGRAM, type); + TEST_ASSERT_EQUAL(IPPROTO_DTLS_1_2, proto); + + return FD; +} + +int z_impl_zsock_socket_coaps_ipv6_then_ipv4_ok(int family, int type, int proto) +{ + switch (z_impl_zsock_socket_fake.call_count) { + case 1: + TEST_ASSERT_EQUAL(AF_INET6, family); + TEST_ASSERT_EQUAL(SOCK_DGRAM, type); + TEST_ASSERT_EQUAL(IPPROTO_DTLS_1_2, proto); + break; + case 2: + TEST_ASSERT_EQUAL(AF_INET, family); + TEST_ASSERT_EQUAL(SOCK_DGRAM, type); + TEST_ASSERT_EQUAL(IPPROTO_DTLS_1_2, proto); + break; + } + + return FD; +} + +int z_impl_zsock_connect_ipv4_ok(int sock, const struct sockaddr *addr, + socklen_t addrlen) +{ + TEST_ASSERT_EQUAL(FD, sock); + TEST_ASSERT_EQUAL(AF_INET, addr->sa_family); + return 0; +} + +int z_impl_zsock_connect_ipv4_ok_then_enetunreach(int sock, const struct sockaddr *addr, + socklen_t addrlen) +{ + TEST_ASSERT_EQUAL(FD, sock); + TEST_ASSERT_EQUAL(AF_INET, addr->sa_family); + + switch (z_impl_zsock_connect_fake.call_count) { + case 1: + return 0; + } + + errno = ENETUNREACH; + return -1; +} + +int z_impl_zsock_connect_ipv6_ok(int sock, const struct sockaddr *addr, + socklen_t addrlen) +{ + TEST_ASSERT_EQUAL(FD, sock); + TEST_ASSERT_EQUAL(AF_INET6, addr->sa_family); + return 0; +} + +int z_impl_zsock_connect_ipv6_then_ipv4_ok(int sock, const struct sockaddr *addr, + socklen_t addrlen) +{ + switch (z_impl_zsock_socket_fake.call_count) { + case 1: + TEST_ASSERT_EQUAL(FD, sock); + TEST_ASSERT_EQUAL(AF_INET6, addr->sa_family); + break; + case 2: + TEST_ASSERT_EQUAL(FD, sock); + TEST_ASSERT_EQUAL(AF_INET, addr->sa_family); + break; + } + + return 0; +} + +int z_impl_zsock_connect_ipv6_fails_ipv4_ok(int sock, const struct sockaddr *addr, + socklen_t addrlen) +{ + TEST_ASSERT_EQUAL(FD, sock); + + if (addr->sa_family == AF_INET6) { + return -EHOSTUNREACH; + } + + return 0; +} + + +int z_impl_zsock_connect_enetunreach(int sock, const struct sockaddr *addr, + socklen_t addrlen) +{ + errno = ENETUNREACH; + return -1; +} + + +int z_impl_zsock_setsockopt_http_ok(int sock, int level, int optname, const void *optval, + socklen_t optlen) +{ + switch (z_impl_zsock_setsockopt_fake.call_count) { + case 1: + TEST_ASSERT_EQUAL(SOL_SOCKET, level); + TEST_ASSERT_EQUAL(SO_BINDTOPDN, optname); + TEST_ASSERT_EQUAL(sizeof(int), optlen); + break; + case 2: + TEST_ASSERT_EQUAL(SOL_SOCKET, level); + TEST_ASSERT_EQUAL(SO_RCVTIMEO, optname); + TEST_ASSERT_EQUAL(sizeof(struct timeval), optlen); + /* Ignore value */ + break; + } + + return 0; +} + +int z_impl_zsock_setsockopt_coap_ok(int sock, int level, int optname, const void *optval, + socklen_t optlen) +{ + switch (z_impl_zsock_setsockopt_fake.call_count) { + case 1: + TEST_ASSERT_EQUAL(SOL_SOCKET, level); + TEST_ASSERT_EQUAL(SO_BINDTOPDN, optname); + TEST_ASSERT_EQUAL(sizeof(int), optlen); + break; + case 2: + TEST_ASSERT_EQUAL(SOL_SOCKET, level); + TEST_ASSERT_EQUAL(SO_SNDTIMEO, optname); + TEST_ASSERT_EQUAL(sizeof(struct timeval), optlen); + /* Ignore value */ + break; + case 3: + TEST_ASSERT_EQUAL(SOL_SOCKET, level); + TEST_ASSERT_EQUAL(SO_RCVTIMEO, optname); + TEST_ASSERT_EQUAL(sizeof(struct timeval), optlen); + /* Ignore value */ + break; + } + + return 0; +} + +int z_impl_zsock_setsockopt_https_ok(int sock, int level, int optname, const void *optval, + socklen_t optlen) +{ + TEST_ASSERT_EQUAL(FD, sock); + + switch (z_impl_zsock_setsockopt_fake.call_count) { + case 1: + TEST_ASSERT_EQUAL(SOL_SOCKET, level); + TEST_ASSERT_EQUAL(SO_BINDTOPDN, optname); + TEST_ASSERT_EQUAL(sizeof(int), optlen); + break; + case 2: + TEST_ASSERT_EQUAL(SOL_TLS, level); + TEST_ASSERT_EQUAL(TLS_PEER_VERIFY, optname); + TEST_ASSERT_EQUAL(4, optlen); + TEST_ASSERT_EQUAL(2, *(int *)optval); + break; + case 3: + TEST_ASSERT_EQUAL(SOL_TLS, level); + TEST_ASSERT_EQUAL(TLS_SEC_TAG_LIST, optname); + TEST_ASSERT_EQUAL(sizeof(sec_tags), optlen); + TEST_ASSERT_EQUAL_MEMORY(sec_tags, optval, sizeof(sec_tags)); + break; + case 4: + TEST_ASSERT_EQUAL(SOL_TLS, level); + TEST_ASSERT_EQUAL(TLS_HOSTNAME, optname); + TEST_ASSERT_EQUAL(strlen(HOSTNAME), optlen); + TEST_ASSERT_EQUAL_MEMORY(HOSTNAME, optval, strlen(HOSTNAME)); + break; + case 5: + TEST_ASSERT_EQUAL(SOL_SOCKET, level); + TEST_ASSERT_EQUAL(SO_RCVTIMEO, optname); + TEST_ASSERT_EQUAL(sizeof(struct timeval), optlen); + /* Ignore value */ + break; + } + + return 0; +} + +int z_impl_zsock_setsockopt_coaps_ok(int sock, int level, int optname, const void *optval, + socklen_t optlen) +{ + TEST_ASSERT_EQUAL(FD, sock); + + switch (z_impl_zsock_setsockopt_fake.call_count) { + case 1: + TEST_ASSERT_EQUAL(SOL_SOCKET, level); + TEST_ASSERT_EQUAL(SO_BINDTOPDN, optname); + TEST_ASSERT_EQUAL(sizeof(int), optlen); + break; + case 2: + TEST_ASSERT_EQUAL(SOL_TLS, level); + TEST_ASSERT_EQUAL(TLS_PEER_VERIFY, optname); + TEST_ASSERT_EQUAL(4, optlen); + TEST_ASSERT_EQUAL(2, *(int *)optval); + break; + case 3: + TEST_ASSERT_EQUAL(SOL_TLS, level); + TEST_ASSERT_EQUAL(TLS_SEC_TAG_LIST, optname); + TEST_ASSERT_EQUAL(sizeof(sec_tags), optlen); + TEST_ASSERT_EQUAL_MEMORY(sec_tags, optval, sizeof(sec_tags)); + break; + case 4: + TEST_ASSERT_EQUAL(SOL_SOCKET, level); + TEST_ASSERT_EQUAL(SO_SNDTIMEO, optname); + TEST_ASSERT_EQUAL(sizeof(struct timeval), optlen); + /* Ignore value */ + break; + case 5: + TEST_ASSERT_EQUAL(SOL_SOCKET, level); + TEST_ASSERT_EQUAL(SO_RCVTIMEO, optname); + TEST_ASSERT_EQUAL(sizeof(struct timeval), optlen); + /* Ignore value */ + break; + } + + return 0; +} + + +int z_impl_zsock_setsockopt_coaps_fail_on_pdn(int sock, int level, int optname, const void *optval, + socklen_t optlen) +{ + TEST_ASSERT_EQUAL(FD, sock); + + if (optname == SO_BINDTOPDN) { + errno = ENOMEM; + return -1; + } + + return 0; +} + +int z_impl_zsock_setsockopt_coaps_fail_on_tls_peer_verify(int sock, int level, int optname, + const void *optval, socklen_t optlen) +{ + TEST_ASSERT_EQUAL(FD, sock); + + if (optname == TLS_PEER_VERIFY) { + errno = ENOMEM; + return -1; + } + + return 0; +} + +int z_impl_zsock_setsockopt_coaps_fail_on_sec_tag_list(int sock, int level, int optname, + const void *optval, socklen_t optlen) +{ + TEST_ASSERT_EQUAL(FD, sock); + + if (optname == TLS_SEC_TAG_LIST) { + errno = ENOMEM; + return -1; + } + + return 0; +} + +int z_impl_zsock_setsockopt_coaps_fail_on_sndtimeo(int sock, int level, int optname, + const void *optval, socklen_t optlen) +{ + TEST_ASSERT_EQUAL(FD, sock); + + if (optname == SO_SNDTIMEO) { + errno = ENOMEM; + return -1; + } + + return 0; +} + +int z_impl_zsock_setsockopt_coaps_fail_on_rcvtimeo(int sock, int level, int optname, + const void *optval, socklen_t optlen) +{ + TEST_ASSERT_EQUAL(FD, sock); + + + if (optname == SO_RCVTIMEO) { + errno = ENOMEM; + return -1; + } + + return 0; +} + +int z_impl_zsock_setsockopt_coaps_fail_on_cid(int sock, int level, int optname, + const void *optval, socklen_t optlen) +{ + TEST_ASSERT_EQUAL(FD, sock); + + + if (optname == TLS_DTLS_CID) { + errno = ENOMEM; + return -1; + } + + return 0; +} + +int z_impl_zsock_setsockopt_coaps_cid_ok(int sock, int level, int optname, const void *optval, + socklen_t optlen) +{ + TEST_ASSERT_EQUAL(FD, sock); + + switch (z_impl_zsock_setsockopt_fake.call_count) { + case 1: + TEST_ASSERT_EQUAL(SOL_SOCKET, level); + TEST_ASSERT_EQUAL(SO_BINDTOPDN, optname); + TEST_ASSERT_EQUAL(sizeof(int), optlen); + break; + case 2: + TEST_ASSERT_EQUAL(SOL_TLS, level); + TEST_ASSERT_EQUAL(TLS_PEER_VERIFY, optname); + TEST_ASSERT_EQUAL(4, optlen); + TEST_ASSERT_EQUAL(2, *(int *)optval); + break; + case 3: + TEST_ASSERT_EQUAL(SOL_TLS, level); + TEST_ASSERT_EQUAL(TLS_SEC_TAG_LIST, optname); + TEST_ASSERT_EQUAL(sizeof(sec_tags), optlen); + TEST_ASSERT_EQUAL_MEMORY(sec_tags, optval, sizeof(sec_tags)); + break; + case 4: + TEST_ASSERT_EQUAL(SOL_TLS, level); + TEST_ASSERT_EQUAL(TLS_DTLS_CID, optname); + TEST_ASSERT_EQUAL(sizeof(uint32_t), optlen); + break; + case 5: + TEST_ASSERT_EQUAL(SOL_SOCKET, level); + TEST_ASSERT_EQUAL(SO_SNDTIMEO, optname); + TEST_ASSERT_EQUAL(sizeof(struct timeval), optlen); + /* Ignore value */ + break; + case 6: + TEST_ASSERT_EQUAL(SOL_SOCKET, level); + TEST_ASSERT_EQUAL(SO_RCVTIMEO, optname); + TEST_ASSERT_EQUAL(sizeof(struct timeval), optlen); + /* Ignore value */ + break; + } + + return 0; +} + +ssize_t z_impl_zsock_sendto_ok(int sock, const void *buf, size_t len, int flags, + const struct sockaddr *dest_addr, socklen_t addrlen) +{ + TEST_ASSERT_EQUAL(FD, sock); + return len; +} + +static ssize_t z_impl_zsock_recvfrom_http_header_then_data( + int sock, void *buf, size_t max_len, int flags, struct sockaddr *src_addr, + socklen_t *addrlen) +{ + TEST_ASSERT_EQUAL(FD, sock); + TEST_ASSERT(sizeof(dl_buf) >= max_len); + + switch (z_impl_zsock_recvfrom_fake.call_count) { + case 1: + memcpy(buf, HTTP_HDR_OK, strlen(HTTP_HDR_OK)); + return strlen(HTTP_HDR_OK); + case 2: + memset(buf, 23, 128); + return 128; + } + + return 0; +} + +static ssize_t z_impl_zsock_recvfrom_http_header_and_payload( + int sock, void *buf, size_t max_len, int flags, struct sockaddr *src_addr, + socklen_t *addrlen) +{ + memcpy(buf, HTTP_HDR_OK_WITH_PAYLOAD, strlen(HTTP_HDR_OK_WITH_PAYLOAD)); + return strlen(HTTP_HDR_OK_WITH_PAYLOAD); +} + +static ssize_t z_impl_zsock_recvfrom_http_header_and_frag_data_w_err( + int sock, void *buf, size_t max_len, int flags, struct sockaddr *src_addr, + socklen_t *addrlen) +{ + switch (z_impl_zsock_recvfrom_fake.call_count) { + case 1: + memcpy(buf, HTTP_HDR_OK, strlen(HTTP_HDR_OK)); + return strlen(HTTP_HDR_OK); + case 2: + memset(buf, 23, 32); + return 32; + case 3: + /* connection reset */ + errno = ECONNRESET; + return -1; + case 4: + memcpy(buf, HTTP_HDR_OK_PROGRESS, strlen(HTTP_HDR_OK_PROGRESS)); + return strlen(HTTP_HDR_OK_PROGRESS); + } + + return 32; +} + +static ssize_t z_impl_zsock_recvfrom_http_header_and_frag_data_peer_close( + int sock, void *buf, size_t max_len, int flags, struct sockaddr *src_addr, + socklen_t *addrlen) +{ + switch (z_impl_zsock_recvfrom_fake.call_count) { + case 1: + memcpy(buf, HTTP_HDR_OK, strlen(HTTP_HDR_OK)); + return strlen(HTTP_HDR_OK); + case 2: + memset(buf, 23, 32); + return 32; + case 3: + /* connection closed */ + return 0; + case 4: + memcpy(buf, HTTP_HDR_OK_PROGRESS, strlen(HTTP_HDR_OK_PROGRESS)); + return strlen(HTTP_HDR_OK_PROGRESS); + } + + return 32; +} + +static ssize_t z_impl_zsock_recvfrom_partial_then_econnreset( + int sock, void *buf, size_t max_len, int flags, struct sockaddr *src_addr, + socklen_t *addrlen) +{ + TEST_ASSERT_EQUAL(FD, sock); + TEST_ASSERT(sizeof(dl_buf) >= max_len); + + switch (z_impl_zsock_recvfrom_fake.call_count) { + case 1: + memcpy(buf, HTTP_HDR_OK, strlen(HTTP_HDR_OK)); + return strlen(HTTP_HDR_OK); + case 2: + memset(buf, 23, 32); + return 32; + } + + return -ECONNRESET; +} + +static ssize_t z_impl_zsock_recvfrom_coap( + int sock, void *buf, size_t max_len, int flags, struct sockaddr *src_addr, + socklen_t *addrlen) +{ + memset(buf, 23, 32); + return 32; +} + +int coap_get_option_int_ok(const struct coap_packet *cpkt, uint16_t code) +{ + return 0; +} + +int coap_block_transfer_init_ok(struct coap_block_context *ctx, + enum coap_block_size block_size, + size_t total_size) +{ + return 0; +} + +void coap_pending_clear_ok(struct coap_pending *pending) +{ + /* empty */ +} + +bool coap_pending_cycle_ok(struct coap_pending *pending) +{ + pending->timeout = 10000; + return true; +} + +bool coap_pending_cycle_5(struct coap_pending *pending) +{ + pending->timeout = 1000 * (5 - coap_pending_cycle_fake.call_count); + + if (pending->timeout) { + return true; + } + + return false; + +} + +int coap_update_from_block_ok(const struct coap_packet *cpkt, + struct coap_block_context *ctx) +{ + return 0; +} + +size_t coap_next_block_empty(const struct coap_packet *cpkt, + struct coap_block_context *ctx) +{ + return 0; +} + +int coap_packet_parse_ok(struct coap_packet *cpkt, uint8_t *data, uint16_t len, + struct coap_option *options, uint8_t opt_num) +{ + return 0; +} + +uint16_t coap_header_get_id_ok(const struct coap_packet *cpkt) +{ + return 0; +} + +uint8_t coap_header_get_type_ack(const struct coap_packet *cpkt) +{ + return COAP_TYPE_ACK; +} + +uint8_t coap_header_get_code_ok(const struct coap_packet *cpkt) +{ + return COAP_RESPONSE_CODE_OK; +} + +uint8_t coap_header_get_code_bad_then_ok(const struct coap_packet *cpkt) +{ + switch (coap_header_get_code_fake.call_count) { + case 1: + return 0xba; + default: return COAP_RESPONSE_CODE_OK; + } + + + return COAP_RESPONSE_CODE_OK; +} + +uint8_t coap_header_get_code_bad(const struct coap_packet *cpkt) +{ + return 0xba; +} + +#define COAP_PAYLOAD "This is the payload" +const uint8_t *coap_packet_get_payload_ok(const struct coap_packet *cpkt, uint16_t *len) +{ + *len = sizeof(COAP_PAYLOAD); + + return COAP_PAYLOAD; +} +int coap_packet_init_ok(struct coap_packet *cpkt, uint8_t *data, uint16_t max_len, + uint8_t ver, uint8_t type, uint8_t token_len, + const uint8_t *token, uint8_t code, uint16_t id) +{ + return 0; +} +uint8_t *coap_next_token_ok(void) +{ + static uint8_t token[COAP_TOKEN_MAX_LEN]; + + return token; +} +int coap_packet_append_option_ok(struct coap_packet *cpkt, uint16_t code, + const uint8_t *value, uint16_t len) +{ + return 0; +} +int coap_append_block2_option_ok(struct coap_packet *cpkt, + struct coap_block_context *ctx) +{ + return 0; +} +int coap_append_size2_option_ok(struct coap_packet *cpkt, + struct coap_block_context *ctx) +{ + return 0; +} + +struct coap_transmission_parameters coap_transmission_params = { + .ack_timeout = 10000, + .coap_backoff_percent = 50, + .max_retransmission = 10, +}; + +struct coap_transmission_parameters coap_get_transmission_parameters_ok(void) +{ + return coap_transmission_params; +} + +K_PIPE_DEFINE(event_pipe, 10*sizeof(struct downloader_evt), + _Alignof(struct downloader_evt)); + +static const char *dl_event_id_str(int evt_id) +{ + switch (evt_id) { + case DOWNLOADER_EVT_FRAGMENT: return "DOWNLOADER_EVT_FRAGMENT"; + case DOWNLOADER_EVT_ERROR: return "DOWNLOADER_EVT_ERROR"; + case DOWNLOADER_EVT_DONE: return "DOWNLOADER_EVT_DONE"; + case DOWNLOADER_EVT_STOPPED: return "DOWNLOADER_EVT_STOPPED"; + case DOWNLOADER_EVT_DEINITIALIZED: return "DOWNLOADER_EVT_DEINITIALIZED"; + } + + return "unknown"; +} + +static int dl_callback(const struct downloader_evt *event) +{ + size_t written; + + TEST_ASSERT(event != NULL); + + printk("event: %s ", dl_event_id_str(event->id)); + if (event->id == DOWNLOADER_EVT_ERROR) { + printk("reason: %d\n", event->error); + /* avoid spamming error events during development */ + k_sleep(K_MSEC(100)); + } else if (event->id == DOWNLOADER_EVT_FRAGMENT) { + printk("len %d\n", event->fragment.len); + } else { + printk("\n"); + } + + k_pipe_put(&event_pipe, (void *)event, sizeof(*event), &written, sizeof(*event), K_FOREVER); + + return 0; +} + +static int dl_callback_abort(const struct downloader_evt *event) +{ + size_t written; + + TEST_ASSERT(event != NULL); + + printk("event: %s\n", dl_event_id_str(event->id)); + if (event->id == DOWNLOADER_EVT_ERROR) { + /* avoid spamming error events during development */ + k_sleep(K_MSEC(100)); + } + k_pipe_put(&event_pipe, (void *)event, sizeof(*event), &written, sizeof(*event), K_FOREVER); + + return 1; /* stop download*/ +} + +static struct downloader_evt dl_wait_for_event(enum downloader_evt_id event, + k_timeout_t timeout) +{ + size_t read; + struct downloader_evt evt; + int err; + + while (true) { + err = k_pipe_get(&event_pipe, &evt, sizeof(evt), &read, sizeof(evt), + timeout); + TEST_ASSERT_EQUAL(0, err); + if (evt.id == event) { + break; + } + } + TEST_ASSERT_EQUAL(evt.id, event); + return evt; +} + +void test_downloader_init_einval(void) +{ + int err; + char buf[1]; + struct downloader dl = {}; + struct downloader_cfg dl_cfg = { + .buf = buf, + .buf_size = ARRAY_SIZE(buf), + .callback = dl_callback, + }; + struct downloader_cfg dl_cfg_no_buf = { + .buf = NULL, + .buf_size = ARRAY_SIZE(buf), + .callback = dl_callback, + }; + struct downloader_cfg dl_cfg_no_buf_len = { + .buf = buf, + .buf_size = 0, + .callback = dl_callback, + }; + struct downloader_cfg dl_cfg_no_cb = { + .buf = buf, + .buf_size = ARRAY_SIZE(buf), + .callback = NULL, + }; + + err = downloader_init(NULL, &dl_cfg); + TEST_ASSERT_EQUAL(-EINVAL, err); + + err = downloader_init(&dl, NULL); + TEST_ASSERT_EQUAL(-EINVAL, err); + + err = downloader_init(&dl, &dl_cfg_no_buf); + TEST_ASSERT_EQUAL(-EINVAL, err); + + err = downloader_init(&dl, &dl_cfg_no_buf_len); + TEST_ASSERT_EQUAL(-EINVAL, err); + + err = downloader_init(&dl, &dl_cfg_no_cb); + TEST_ASSERT_EQUAL(-EINVAL, err); +} + +void test_downloader_deinit_einval(void) +{ + int err; + + err = downloader_deinit(NULL); + TEST_ASSERT_EQUAL(-EINVAL, err); +} + +void test_downloader_deinit_eperm(void) +{ + int err; + struct downloader dl = {}; + + err = downloader_deinit(&dl); + TEST_ASSERT_EQUAL(-EPERM, err); + + dl.state = 0xffffffff; + /* bad state */ + err = downloader_deinit(&dl); + TEST_ASSERT_EQUAL(-EPERM, err); +} + +void test_downloader_get_eperm(void) +{ + int err; + + err = downloader_get(&dl, &dl_host_cfg, HTTP_URL, 0); + TEST_ASSERT_EQUAL(-EPERM, err); +} + +void test_downloader_get_http(void) +{ + int err; + struct downloader_evt evt; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ipv6_fail_ipv4_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv4; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_http_ipv4_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv4_ok; + z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_http_ok; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_http_header_then_data; + + + err = downloader_get(&dl, &dl_host_cfg, HTTP_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_DONE, K_SECONDS(3)); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get_https(void) +{ + int err; + struct downloader_evt evt; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv6; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_https_ipv6_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv6_ok; + z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_https_ok; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_http_header_then_data; + + + err = downloader_get(&dl, &dl_host_conf_w_sec_tags, HTTPS_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_DONE, K_SECONDS(3)); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get_http_connect_enetunreach(void) +{ + int err; + struct downloader_evt evt; + + err = downloader_init(&dl, &dl_cfg_cb_abort); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ipv6_fail_ipv4_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv4; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_http_ipv4_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_enetunreach; + z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_http_ok; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_http_header_then_data; + + + err = downloader_get(&dl, &dl_host_cfg, HTTP_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_ERROR, K_SECONDS(1)); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get_http_getaddr_failed(void) +{ + int err; + struct downloader_evt evt; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_enetunreach; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv4; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_http_ipv4_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv4_ok; + z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_http_ok; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_http_header_then_data; + + err = downloader_get(&dl, &dl_host_cfg, HTTP_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_ERROR, K_SECONDS(3)); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get_default_proto_http(void) +{ + int err; + struct downloader_evt evt; + + err = downloader_init(&dl, &dl_cfg_cb_abort); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ipv6_fail_ipv4_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv4; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_http_ipv4_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv4_ok; + z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_http_ok; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_http_header_then_data; + + err = downloader_get(&dl, &dl_host_cfg, NO_PROTO_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_DONE, K_SECONDS(3)); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get_default_proto_https(void) +{ + int err; + struct downloader_evt evt; + + err = downloader_init(&dl, &dl_cfg_cb_abort); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv6; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_https_ipv6_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv6_ok; + z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_https_ok; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_http_header_then_data; + + err = downloader_get(&dl, &dl_host_conf_w_sec_tags, NO_PROTO_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_DONE, K_SECONDS(3)); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get_reconnect_on_socket_error(void) +{ + int err; + struct downloader_evt evt; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv6; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_http_ipv6_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv6_ok; + z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_http_ok; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = + z_impl_zsock_recvfrom_http_header_and_frag_data_w_err; + + err = downloader_get(&dl, &dl_host_cfg, HTTP_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_DONE, K_SECONDS(3)); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get_reconnect_on_peer_close(void) +{ + int err; + struct downloader_evt evt; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv6; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_http_ipv6_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv6_ok; + z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_http_ok; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = + z_impl_zsock_recvfrom_http_header_and_frag_data_peer_close; + + err = downloader_get(&dl, &dl_host_cfg, HTTP_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_DONE, K_SECONDS(3)); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get_coap(void) +{ + int err; + struct downloader_evt evt; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv6; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_coap_ipv6_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv6_ok; + z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_coap_ok; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_coap; + + coap_get_transmission_parameters_fake.custom_fake = coap_get_transmission_parameters_ok; + coap_pending_cycle_fake.custom_fake = coap_pending_cycle_ok; + coap_header_get_type_fake.custom_fake = coap_header_get_type_ack; + coap_header_get_code_fake.custom_fake = coap_header_get_code_ok; + coap_packet_get_payload_fake.custom_fake = coap_packet_get_payload_ok; + + err = downloader_get(&dl, &dl_host_cfg, COAP_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_DONE, K_SECONDS(3)); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get_coaps(void) +{ + int err; + struct downloader_evt evt; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv6; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_coaps_ipv6_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv6_ok; + z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_coaps_ok; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_coap; + + coap_get_transmission_parameters_fake.custom_fake = coap_get_transmission_parameters_ok; + coap_pending_cycle_fake.custom_fake = coap_pending_cycle_ok; + coap_header_get_type_fake.custom_fake = coap_header_get_type_ack; + coap_header_get_code_fake.custom_fake = coap_header_get_code_ok; + coap_packet_get_payload_fake.custom_fake = coap_packet_get_payload_ok; + + err = downloader_get(&dl, &dl_host_conf_w_sec_tags, COAPS_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_DONE, K_SECONDS(3)); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get_coaps_setsockopt_fail_pdn(void) +{ + int err; + struct downloader_evt evt; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv6_then_ipv4; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_coaps_ipv6_then_ipv4_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv6_ok; + z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_coaps_fail_on_pdn; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_coap; + + coap_get_transmission_parameters_fake.custom_fake = coap_get_transmission_parameters_ok; + coap_pending_cycle_fake.custom_fake = coap_pending_cycle_ok; + coap_header_get_type_fake.custom_fake = coap_header_get_type_ack; + coap_header_get_code_fake.custom_fake = coap_header_get_code_ok; + coap_packet_get_payload_fake.custom_fake = coap_packet_get_payload_ok; + + err = downloader_get(&dl, &dl_host_conf_w_sec_tags, COAPS_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_ERROR, K_SECONDS(3)); + + err = downloader_cancel(&dl); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get_coaps_setsockopt_fail_sec_tag_list(void) +{ + int err; + struct downloader_evt evt; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv6_then_ipv4; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_coaps_ipv6_then_ipv4_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv6_then_ipv4_ok; + z_impl_zsock_setsockopt_fake.custom_fake = + z_impl_zsock_setsockopt_coaps_fail_on_sec_tag_list; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_coap; + + coap_get_transmission_parameters_fake.custom_fake = coap_get_transmission_parameters_ok; + coap_pending_cycle_fake.custom_fake = coap_pending_cycle_ok; + coap_header_get_type_fake.custom_fake = coap_header_get_type_ack; + coap_header_get_code_fake.custom_fake = coap_header_get_code_ok; + coap_packet_get_payload_fake.custom_fake = coap_packet_get_payload_ok; + + err = downloader_get(&dl, &dl_host_conf_w_sec_tags, COAPS_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_ERROR, K_SECONDS(3)); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get_coaps_setsockopt_fail_sndtimeo(void) +{ + int err; + struct downloader_evt evt; + struct downloader_transport_coap_cfg coap_cfg = { + .block_size = COAP_BLOCK_512, + .max_retransmission = 10, + }; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + err = downloader_transport_coap_set_config(&dl, &coap_cfg); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv6; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_coaps_ipv6_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv6_ok; + z_impl_zsock_setsockopt_fake.custom_fake = + z_impl_zsock_setsockopt_coaps_fail_on_sndtimeo; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_coap; + + coap_get_transmission_parameters_fake.custom_fake = coap_get_transmission_parameters_ok; + coap_pending_cycle_fake.custom_fake = coap_pending_cycle_ok; + coap_header_get_type_fake.custom_fake = coap_header_get_type_ack; + coap_header_get_code_fake.custom_fake = coap_header_get_code_ok; + coap_packet_get_payload_fake.custom_fake = coap_packet_get_payload_ok; + + err = downloader_get(&dl, &dl_host_conf_w_sec_tags, COAPS_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_ERROR, K_SECONDS(3)); + + err = downloader_cancel(&dl); + evt = dl_wait_for_event(DOWNLOADER_EVT_STOPPED, K_SECONDS(3)); + + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get_coaps_setsockopt_fail_rcvtimeo(void) +{ + int err; + struct downloader_evt evt; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv6_then_ipv4; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_coaps_ipv6_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv6_ok; + z_impl_zsock_setsockopt_fake.custom_fake = + z_impl_zsock_setsockopt_coaps_fail_on_rcvtimeo; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_coap; + + coap_get_transmission_parameters_fake.custom_fake = coap_get_transmission_parameters_ok; + coap_pending_cycle_fake.custom_fake = coap_pending_cycle_ok; + coap_header_get_type_fake.custom_fake = coap_header_get_type_ack; + coap_header_get_code_fake.custom_fake = coap_header_get_code_ok; + coap_packet_get_payload_fake.custom_fake = coap_packet_get_payload_ok; + + err = downloader_get(&dl, &dl_host_conf_w_sec_tags, COAPS_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_ERROR, K_SECONDS(3)); + + err = downloader_cancel(&dl); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get_coaps_setsockopt_fail_cid(void) +{ + int err; + struct downloader_evt evt; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv6_then_ipv4; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_coaps_ipv6_then_ipv4_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv6_ok; + z_impl_zsock_setsockopt_fake.custom_fake = + z_impl_zsock_setsockopt_coaps_fail_on_cid; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_coap; + + coap_get_transmission_parameters_fake.custom_fake = coap_get_transmission_parameters_ok; + coap_pending_cycle_fake.custom_fake = coap_pending_cycle_ok; + coap_header_get_type_fake.custom_fake = coap_header_get_type_ack; + coap_header_get_code_fake.custom_fake = coap_header_get_code_ok; + coap_packet_get_payload_fake.custom_fake = coap_packet_get_payload_ok; + + err = downloader_get(&dl, &dl_host_conf_w_sec_tags_and_cid, COAPS_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_ERROR, K_SECONDS(3)); + + err = downloader_cancel(&dl); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get_coaps_cid(void) +{ + int err; + struct downloader_evt evt; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv6; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_coaps_ipv6_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv6_ok; + z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_coaps_cid_ok; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_coap; + + coap_get_transmission_parameters_fake.custom_fake = coap_get_transmission_parameters_ok; + coap_pending_cycle_fake.custom_fake = coap_pending_cycle_ok; + coap_header_get_type_fake.custom_fake = coap_header_get_type_ack; + coap_header_get_code_fake.custom_fake = coap_header_get_code_ok; + coap_packet_get_payload_fake.custom_fake = coap_packet_get_payload_ok; + + err = downloader_get(&dl, &dl_host_conf_w_sec_tags_and_cid, COAPS_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_DONE, K_SECONDS(3)); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get_coap_one_bad_header_code(void) +{ + int err; + struct downloader_evt evt; + + err = downloader_init(&dl, &dl_cfg_cb_abort); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv6; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_coap_ipv6_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv6_ok; + z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_coap_ok; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_coap; + + coap_get_transmission_parameters_fake.custom_fake = coap_get_transmission_parameters_ok; + coap_pending_cycle_fake.custom_fake = coap_pending_cycle_ok; + coap_header_get_type_fake.custom_fake = coap_header_get_type_ack; + coap_header_get_code_fake.custom_fake = coap_header_get_code_bad_then_ok; + coap_packet_get_payload_fake.custom_fake = coap_packet_get_payload_ok; + + err = downloader_get(&dl, &dl_host_cfg, COAP_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_DONE, K_SECONDS(3)); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get_coap_bad_header_code_timeout(void) +{ + int err; + struct downloader_evt evt; + + err = downloader_init(&dl, &dl_cfg_cb_abort); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv6; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_coap_ipv6_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv6_ok; + z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_coap_ok; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_coap; + + coap_get_transmission_parameters_fake.custom_fake = coap_get_transmission_parameters_ok; + coap_pending_cycle_fake.custom_fake = coap_pending_cycle_5; + coap_header_get_type_fake.custom_fake = coap_header_get_type_ack; + coap_header_get_code_fake.custom_fake = coap_header_get_code_bad; + coap_packet_get_payload_fake.custom_fake = coap_packet_get_payload_ok; + + err = downloader_get(&dl, &dl_host_cfg, COAP_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_ERROR, K_SECONDS(3)); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get_einval(void) +{ + int err; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + err = downloader_get(NULL, &dl_host_cfg, HTTP_URL, 0); + TEST_ASSERT_EQUAL(-EINVAL, err); + + err = downloader_get(&dl, NULL, HTTP_URL, 0); + TEST_ASSERT_EQUAL(-EINVAL, err); + + err = downloader_get(&dl, &dl_host_cfg, NULL, 0); + TEST_ASSERT_EQUAL(-EINVAL, err); + + err = downloader_get(&dl, &dl_host_cfg, BAD_URL_NO_FILE, 0); + TEST_ASSERT_EQUAL(-EINVAL, err); + + /* https url, no sec tag specified */ + err = downloader_get(&dl, &dl_host_cfg, HTTPS_URL, 0); + TEST_ASSERT_EQUAL(-EINVAL, err); + + /* coaps url, no sec tag specified */ + err = downloader_get(&dl, &dl_host_cfg, COAPS_URL, 0); + TEST_ASSERT_EQUAL(-EINVAL, err); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get_eprotonotsupport(void) +{ + int err; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + err = downloader_get(&dl, &dl_host_cfg, BAD_URL, 0); + TEST_ASSERT_EQUAL(-EPROTONOSUPPORT, err); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get(void) +{ + int err; + struct downloader_evt evt; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv6; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_http_ipv6_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv6_ok; + z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_http_ok; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_http_header_then_data; + + + err = downloader_get(&dl, &dl_host_cfg, HTTP_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_DONE, K_SECONDS(3)); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get_two_files_same_host(void) +{ + int err; + struct downloader_evt evt; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv6; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_http_ipv6_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv6_ok; + z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_http_ok; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_http_header_then_data; + + + err = downloader_get(&dl, &dl_host_cfg, HTTP_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_DONE, K_SECONDS(3)); + + z_impl_zsock_recvfrom_fake.call_count = 0; + + err = downloader_get(&dl, &dl_host_cfg, HTTP_URL_FILE2, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_DONE, K_SECONDS(3)); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); + + /* make sure we only connected once */ + TEST_ASSERT_EQUAL(1, z_impl_zsock_connect_fake.call_count); +} + +void test_downloader_get_two_files_different_host(void) +{ + int err; + struct downloader_evt evt; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv6; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_http_ipv6_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv6_ok; + z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_http_ok; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_http_header_then_data; + + + err = downloader_get(&dl, &dl_host_cfg, HTTP_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_DONE, K_SECONDS(3)); + + z_impl_zsock_recvfrom_fake.call_count = 0; + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server2_ok; + + err = downloader_get(&dl, &dl_host_cfg, HTTP_URL_HOST2, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_DONE, K_SECONDS(3)); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); + + /* make sure we reconnected */ + TEST_ASSERT_EQUAL(2, z_impl_zsock_connect_fake.call_count); +} + +void test_downloader_get_hdr_and_payload(void) +{ + int err; + struct downloader_evt evt; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv6; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_http_ipv6_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv6_ok; + z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_http_ok; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_http_header_and_payload; + + + err = downloader_get(&dl, &dl_host_cfg, HTTP_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_FRAGMENT, K_SECONDS(3)); + TEST_ASSERT_EQUAL(20, evt.fragment.len); + TEST_ASSERT_EQUAL_MEMORY(PAYLOAD, evt.fragment.buf, evt.fragment.len); + + evt = dl_wait_for_event(DOWNLOADER_EVT_DONE, K_SECONDS(3)); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get_ipv6_fails_to_connect_ipv4_success(void) +{ + int err; + struct downloader_evt evt; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv6_then_ipv4; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_http_ipv6_then_ipv4; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv6_fails_ipv4_ok; + z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_http_ok; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_http_header_then_data; + + err = downloader_get(&dl, &dl_host_cfg, HTTP_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_DONE, K_SECONDS(3)); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get_ipv4_specific_ok(void) +{ + int err; + struct downloader_evt evt; + static struct downloader_host_cfg dl_host_conf_ipv4 = { + .pdn_id = 1, + .family = AF_INET, + }; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv4; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_http_ipv4_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv4_ok; + z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_http_ok; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_http_header_then_data; + + err = downloader_get(&dl, &dl_host_conf_ipv4, HTTP_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_DONE, K_SECONDS(3)); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get_econnreset_reconnect_fails(void) +{ + int err; + struct downloader_evt evt; + static struct downloader_host_cfg dl_host_conf_ipv4 = { + .pdn_id = 1, + .family = AF_INET, + }; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv4; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_http_ipv4_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv4_ok_then_enetunreach; + z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_http_ok; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_partial_then_econnreset; + + err = downloader_get(&dl, &dl_host_conf_ipv4, HTTP_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_FRAGMENT, K_SECONDS(3)); + + evt = dl_wait_for_event(DOWNLOADER_EVT_ERROR, K_SECONDS(3)); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_cancel_einval(void) +{ + int err; + + err = downloader_cancel(NULL); + TEST_ASSERT_EQUAL(-EINVAL, err); + +} + +void test_downloader_cancel_eperm(void) +{ + int err; + + err = downloader_cancel(&dl); + TEST_ASSERT_EQUAL(-EPERM, err); +} + +void test_downloader_cancel(void) +{ + int err; + struct downloader_evt evt; + static struct downloader_host_cfg dl_host_conf_ipv4 = { + .pdn_id = 1, + .family = AF_INET, + }; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv4; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_http_ipv4_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv4_ok; + z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_http_ok; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_partial_then_econnreset; + + err = downloader_get(&dl, &dl_host_conf_ipv4, HTTP_URL, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_FRAGMENT, K_SECONDS(3)); + + err = downloader_cancel(&dl); + TEST_ASSERT_EQUAL(0, err); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get_with_host_and_file_einval(void) +{ + int err; + + err = downloader_init(&dl, &dl_cfg_small_buffer); + TEST_ASSERT_EQUAL(0, err); + + err = downloader_get_with_host_and_file(NULL, &dl_host_cfg, HTTP_HOST, FILE_PATH, 0); + TEST_ASSERT_EQUAL(-EINVAL, err); + + err = downloader_get_with_host_and_file(&dl, NULL, HTTP_HOST, FILE_PATH, 0); + TEST_ASSERT_EQUAL(-EINVAL, err); + + err = downloader_get_with_host_and_file(&dl, &dl_host_cfg, NULL, FILE_PATH, 0); + TEST_ASSERT_EQUAL(-EINVAL, err); + + err = downloader_get_with_host_and_file(&dl, &dl_host_cfg, HTTP_HOST, NULL, 0); + TEST_ASSERT_EQUAL(-EINVAL, err); + + err = downloader_get_with_host_and_file(&dl, &dl_host_cfg, HTTP_HOST, FILE_PATH_LONG, 0); + TEST_ASSERT_EQUAL(-EINVAL, err); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_get_with_host_and_file_eperm(void) +{ + int err; + + err = downloader_get_with_host_and_file(&dl, &dl_host_cfg, HTTP_HOST, FILE_PATH, 0); + TEST_ASSERT_EQUAL(-EPERM, err); +} + +void test_downloader_get_with_host_and_file(void) +{ + int err; + struct downloader_evt evt; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv6; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_http_ipv6_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv6_ok; + z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_http_ok; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_http_header_then_data; + + + err = downloader_get_with_host_and_file(&dl, &dl_host_cfg, HTTP_HOST, FILE_PATH, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_DONE, K_SECONDS(3)); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_file_size_get_einval(void) +{ + int err; + size_t fs; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + err = downloader_file_size_get(NULL, &fs); + TEST_ASSERT_EQUAL(-EINVAL, err); + + err = downloader_file_size_get(&dl, NULL); + TEST_ASSERT_EQUAL(-EINVAL, err); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_file_size_get_eperm(void) +{ + int err; + size_t fs; + + err = downloader_file_size_get(&dl, &fs); + TEST_ASSERT_EQUAL(-EPERM, err); +} + +void test_downloader_file_size_get(void) +{ + int err; + struct downloader_evt evt; + size_t fs; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv6; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_http_ipv6_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv6_ok; + z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_http_ok; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_http_header_then_data; + + + err = downloader_get_with_host_and_file(&dl, &dl_host_cfg, HTTP_HOST, FILE_PATH, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_FRAGMENT, K_SECONDS(3)); + + err = downloader_file_size_get(&dl, &fs); + TEST_ASSERT_EQUAL(0, err); + TEST_ASSERT_EQUAL(128, fs); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_downloaded_size_get_einval(void) +{ + int err; + size_t fs; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + err = downloader_downloaded_size_get(NULL, &fs); + TEST_ASSERT_EQUAL(-EINVAL, err); + + err = downloader_downloaded_size_get(&dl, NULL); + TEST_ASSERT_EQUAL(-EINVAL, err); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void test_downloader_downloaded_size_get_eperm(void) +{ + int err; + size_t fs; + + err = downloader_downloaded_size_get(&dl, &fs); + TEST_ASSERT_EQUAL(-EPERM, err); +} + +void test_downloader_downloaded_size_get(void) +{ + int err; + struct downloader_evt evt; + size_t fs; + + err = downloader_init(&dl, &dl_cfg); + TEST_ASSERT_EQUAL(0, err); + + zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok; + zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv6; + z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_http_ipv6_ok; + z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv6_ok; + z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_http_ok; + z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok; + z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_http_header_then_data; + + err = downloader_downloaded_size_get(&dl, &fs); + TEST_ASSERT_EQUAL(0, err); + TEST_ASSERT_EQUAL(0, fs); + + err = downloader_get_with_host_and_file(&dl, &dl_host_cfg, HTTP_HOST, FILE_PATH, 0); + TEST_ASSERT_EQUAL(0, err); + + evt = dl_wait_for_event(DOWNLOADER_EVT_FRAGMENT, K_SECONDS(3)); + + err = downloader_downloaded_size_get(&dl, &fs); + TEST_ASSERT_EQUAL(0, err); + TEST_ASSERT_EQUAL(128, fs); + + downloader_deinit(&dl); + dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1)); +} + +void setUp(void) +{ + RESET_FAKE(z_impl_zsock_setsockopt); + RESET_FAKE(z_impl_zsock_socket); + RESET_FAKE(z_impl_zsock_connect); + RESET_FAKE(z_impl_zsock_close); + RESET_FAKE(z_impl_zsock_send); + RESET_FAKE(z_impl_zsock_recv); + RESET_FAKE(zsock_getaddrinfo); + RESET_FAKE(zsock_freeaddrinfo); + RESET_FAKE(z_impl_zsock_inet_pton); + RESET_FAKE(z_impl_net_addr_ntop); + RESET_FAKE(z_impl_zsock_sendto); + RESET_FAKE(z_impl_zsock_recvfrom); + RESET_FAKE(z_impl_sys_rand_get); + + RESET_FAKE(coap_get_option_int); + RESET_FAKE(coap_block_transfer_init); + RESET_FAKE(coap_pending_clear); + RESET_FAKE(coap_pending_cycle); + RESET_FAKE(coap_update_from_block); + RESET_FAKE(coap_next_block); + RESET_FAKE(coap_packet_parse); + RESET_FAKE(coap_header_get_id); + RESET_FAKE(coap_header_get_type); + RESET_FAKE(coap_header_get_code); + RESET_FAKE(coap_packet_get_payload); + RESET_FAKE(coap_packet_init); + RESET_FAKE(coap_next_token); + RESET_FAKE(coap_packet_append_option); + RESET_FAKE(coap_append_block2_option); + RESET_FAKE(coap_append_size2_option); + RESET_FAKE(coap_get_transmission_parameters); + RESET_FAKE(coap_pending_init); + + k_pipe_flush(&event_pipe); +} + +void tearDown(void) +{ +} + +/* It is required to be added to each test. That is because unity's + * main may return nonzero, while zephyr's main currently must + * return 0 in all cases (other values are reserved). + */ +extern int unity_main(void); + +int main(void) +{ + (void)unity_main(); + + return 0; +} diff --git a/tests/subsys/net/lib/downloader/testcase.yaml b/tests/subsys/net/lib/downloader/testcase.yaml new file mode 100644 index 000000000000..2d687db6193a --- /dev/null +++ b/tests/subsys/net/lib/downloader/testcase.yaml @@ -0,0 +1,11 @@ +tests: + net.lib.downloader: + sysbuild: true + tags: + - fota + - sysbuild + - ci_tests_subsys_net + platform_allow: + - native_sim + integration_platforms: + - native_sim diff --git a/tests/subsys/net/lib/fota_download/CMakeLists.txt b/tests/subsys/net/lib/fota_download/CMakeLists.txt index 16cf827e0597..e61aa1a1523d 100644 --- a/tests/subsys/net/lib/fota_download/CMakeLists.txt +++ b/tests/subsys/net/lib/fota_download/CMakeLists.txt @@ -25,15 +25,16 @@ target_include_directories(app ${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/fota_download/include ${ZEPHYR_NRF_MODULE_DIR}/include/net/ ${ZEPHYR_NRF_MODULE_DIR}/subsys/dfu/include - ${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/download_client/include + ${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/downloader/include . # To get 'pm_config.h' ) target_compile_options(app PRIVATE - -DCONFIG_DOWNLOAD_CLIENT_BUF_SIZE=500 - -DCONFIG_DOWNLOAD_CLIENT_STACK_SIZE=500 - -DCONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE=192 + -DCONFIG_DOWNLOADER_STACK_SIZE=500 + -DCONFIG_DOWNLOADER_MAX_FILENAME_SIZE=192 + -DCONFIG_DOWNLOADER_MAX_HOSTNAME_SIZE=128 + -DCONFIG_DOWNLOADER_TRANSPORT_PARAMS_SIZE=128 -DCONFIG_FW_MAGIC_LEN=32 -DABI_INFO_MAGIC=0xdededede -DCONFIG_FW_FIRMWARE_INFO_OFFSET=0x200 @@ -44,4 +45,5 @@ target_compile_options(app -DCONFIG_FOTA_DOWNLOAD_FILE_NAME_LENGTH=128 -DCONFIG_FOTA_DOWNLOAD_HOST_NAME_LENGTH=128 -DCONFIG_FOTA_DOWNLOAD_SEC_TAG_LIST_SIZE_MAX=5 + -DCONFIG_FOTA_DOWNLOAD_BUF_SZ=2048 ) diff --git a/tests/subsys/net/lib/fota_download/src/test_fota_download.c b/tests/subsys/net/lib/fota_download/src/test_fota_download.c index 411523c03fc5..4144ec2ee7df 100644 --- a/tests/subsys/net/lib/fota_download/src/test_fota_download.c +++ b/tests/subsys/net/lib/fota_download/src/test_fota_download.c @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include #include @@ -24,7 +24,7 @@ static char buf[1024]; #define ARBITRARY_IMAGE_OFFSET 512 /* Stubs and mocks */ -static const char *download_client_start_file; +static const char *downloader_get_file; static bool spm_s0_active_retval; K_SEM_DEFINE(download_with_offset_sem, 0, 1); @@ -33,7 +33,7 @@ static bool fail_on_offset_get; static bool fail_on_connect; static bool fail_on_start; static bool download_with_offset_success; -static download_client_callback_t download_client_event_handler; +static downloader_callback_t downloader_event_handler; K_SEM_DEFINE(stop_sem, 0, 1); int dfu_target_init(int img_type, int img_num, size_t file_size, dfu_target_callback_t cb) @@ -81,16 +81,14 @@ int dfu_target_schedule_update(int img_num) return 0; } -int download_client_file_size_get(struct download_client *client, size_t *size) +int downloader_file_size_get(struct downloader *client, size_t *size) { return 0; } -int download_client_init(struct download_client *client, - download_client_callback_t callback) +int downloader_init(struct downloader *client, struct downloader_cfg *dl_cfg) { - download_client_event_handler = callback; - client->fd = -1; + downloader_event_handler = dl_cfg->callback; return 0; } @@ -99,16 +97,15 @@ enum dfu_target_image_type dfu_target_smp_img_type_check(const void *const buf, return DFU_TARGET_IMAGE_TYPE_SMP; } -int download_client_get(struct download_client *client, const char *host, - const struct download_client_cfg *config, const char *file, size_t from) +int downloader_get_with_host_and_file(struct downloader *dl, + const struct downloader_host_cfg *dl_host_cfg, + const char *host, const char *file, size_t from) { if (fail_on_connect == true) { return -1; } - /* Mark connection */ - client->fd = 1; - download_client_start_file = file; + downloader_get_file = file; if (fail_on_start == true) { return -1; @@ -122,16 +119,13 @@ int download_client_get(struct download_client *client, const char *host, return 0; } -int download_client_disconnect(struct download_client *client) +int downloader_cancel(struct downloader *client) { - const struct download_client_evt evt = { - .id = DOWNLOAD_CLIENT_EVT_CLOSED, + const struct downloader_evt evt = { + .id = DOWNLOADER_EVT_STOPPED, }; - if (client->fd == -1) { - return -EINVAL; - } - client->fd = -1; - download_client_event_handler(&evt); + + downloader_event_handler(&evt); return 0; } @@ -259,7 +253,7 @@ static void init(void) fail_on_offset_get = false; fail_on_connect = false; fail_on_start = false; - download_client_start_file = NULL; + downloader_get_file = NULL; spm_s0_active_retval = false; k_sem_reset(&stop_sem); @@ -293,7 +287,7 @@ static void test_fota_download_any_generic(const char * const resource_locator, zassert_equal(err, 0, NULL); /* Verify that the correct resource was selected */ - zassert_true(strcmp(download_client_start_file, expected_selection) == 0, NULL); + zassert_true(strcmp(downloader_get_file, expected_selection) == 0, NULL); /* Verify that the download can be canceled with no isse */ err = fota_download_cancel(); @@ -340,8 +334,8 @@ ZTEST(fota_download_tests, test_download_with_offset) uint8_t fragment_buf[1] = {0}; size_t fragment_len = 1; - const struct download_client_evt evt = { - .id = DOWNLOAD_CLIENT_EVT_FRAGMENT, + const struct downloader_evt evt = { + .id = DOWNLOADER_EVT_FRAGMENT, .fragment = { .buf = fragment_buf, .len = fragment_len, @@ -354,7 +348,7 @@ ZTEST(fota_download_tests, test_download_with_offset) err = fota_download_any(BASE_DOMAIN, buf, NO_TLS, 0, 0, 0); zassert_ok(err, NULL); - err = download_client_event_handler(&evt); + err = downloader_event_handler(&evt); zassert_equal(err, -1, NULL); fail_on_offset_get = true; @@ -373,7 +367,7 @@ ZTEST(fota_download_tests, test_download_with_offset) err = fota_download_any(BASE_DOMAIN, buf, NO_TLS, 0, 0, 0); zassert_ok(err, NULL); - err = download_client_event_handler(&evt); + err = downloader_event_handler(&evt); zassert_equal(err, -1, NULL); fail_on_connect = true; @@ -391,7 +385,7 @@ ZTEST(fota_download_tests, test_download_with_offset) err = fota_download_any(BASE_DOMAIN, buf, NO_TLS, 0, 0, 0); zassert_ok(err, NULL); - err = download_client_event_handler(&evt); + err = downloader_event_handler(&evt); zassert_equal(err, -1, NULL); fail_on_start = true; @@ -407,7 +401,7 @@ ZTEST(fota_download_tests, test_download_with_offset) err = fota_download_any(BASE_DOMAIN, buf, NO_TLS, 0, 0, 0); zassert_ok(err, NULL); - err = download_client_event_handler(&evt); + err = downloader_event_handler(&evt); zassert_equal(err, -1, NULL); download_with_offset_success = false; diff --git a/tests/subsys/net/lib/lwm2m_client_utils/CMakeLists.txt b/tests/subsys/net/lib/lwm2m_client_utils/CMakeLists.txt index f9216633e624..57a583422203 100644 --- a/tests/subsys/net/lib/lwm2m_client_utils/CMakeLists.txt +++ b/tests/subsys/net/lib/lwm2m_client_utils/CMakeLists.txt @@ -33,8 +33,10 @@ set(options -DCONFIG_LTE_PSM_REQ_RAT="00001111" -DCONFIG_LTE_EDRX_REQ_VALUE_LTE_M="0001" -DCONFIG_LTE_PTW_VALUE_LTE_M="0000" - -DCONFIG_DOWNLOAD_CLIENT_BUF_SIZE=2048 - -DCONFIG_DOWNLOAD_CLIENT_STACK_SIZE=4096 + -DCONFIG_DOWNLOADER_STACK_SIZE=4096 + -DCONFIG_DOWNLOADER_MAX_HOSTNAME_SIZE=128 + -DCONFIG_DOWNLOADER_MAX_FILENAME_SIZE=128 + -DCONFIG_DOWNLOADER_TRANSPORT_PARAMS_SIZE=128 -DCONFIG_LWM2M_FIRMWARE_UPDATE_OBJ_SUPPORT -DCONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT -DCONFIG_DFU_TARGET_MCUBOOT diff --git a/tests/subsys/net/lib/lwm2m_fota_utils/CMakeLists.txt b/tests/subsys/net/lib/lwm2m_fota_utils/CMakeLists.txt index faceb800e949..470fa1e5ae86 100644 --- a/tests/subsys/net/lib/lwm2m_fota_utils/CMakeLists.txt +++ b/tests/subsys/net/lib/lwm2m_fota_utils/CMakeLists.txt @@ -32,8 +32,10 @@ set(options -DCONFIG_LWM2M_CLIENT_UTILS_NEIGHBOUR_CELL_LISTENER -DCONFIG_LWM2M_CLIENT_UTILS_CONN_MON_OBJ_SUPPORT -DCONFIG_LTE_LC_TAU_PRE_WARNING_NOTIFICATIONS - -DCONFIG_DOWNLOAD_CLIENT_BUF_SIZE=2048 - -DCONFIG_DOWNLOAD_CLIENT_STACK_SIZE=4096 + -DCONFIG_DOWNLOADER_STACK_SIZE=4096 + -DCONFIG_DOWNLOADER_MAX_HOSTNAME_SIZE=128 + -DCONFIG_DOWNLOADER_MAX_FILENAME_SIZE=128 + -DCONFIG_DOWNLOADER_TRANSPORT_PARAMS_SIZE=128 -DCONFIG_LWM2M_FIRMWARE_UPDATE_OBJ_SUPPORT -DCONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT -DCONFIG_DFU_TARGET_MCUBOOT=y diff --git a/tests/subsys/net/lib/mcumgr_smp_client/CMakeLists.txt b/tests/subsys/net/lib/mcumgr_smp_client/CMakeLists.txt index d97350fb1c75..118183771b2f 100644 --- a/tests/subsys/net/lib/mcumgr_smp_client/CMakeLists.txt +++ b/tests/subsys/net/lib/mcumgr_smp_client/CMakeLists.txt @@ -17,12 +17,12 @@ target_sources(app ${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/mcumgr_smp_client/src/mcumgr_smp_client.c ${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/fota_download/src/util/fota_download_util.c ${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/fota_download/src/util/fota_download_smp.c - ${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/download_client/src/parse.c + ${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/downloader/src/dl_parse.c ) set(includes "src/" -${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/download_client/include +${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/downloader/include ) target_include_directories(app @@ -40,10 +40,12 @@ target_compile_options(app -DCONFIG_NRF_MCUMGR_SMP_CLIENT_LOG_LEVEL=2 -DCONFIG_FOTA_DOWNLOAD_LOG_LEVEL=2 -DCONFIG_FOTA_DOWNLOAD_FILE_NAME_LENGTH=128 - -DCONFIG_FOTA_DOWNLOAD_HOST_NAME_LENGTH=128 + -DCONFIG_FOTA_DOWNLOAD_HOST_NAME_LENGTH=256 -DCONFIG_FOTA_DOWNLOAD_RESOURCE_LOCATOR_LENGTH=512 - -DCONFIG_DOWNLOAD_CLIENT_BUF_SIZE=2048 - -DCONFIG_DOWNLOAD_CLIENT_STACK_SIZE=1024 + -DCONFIG_DOWNLOADER_MAX_HOSTNAME_SIZE=128 + -DCONFIG_DOWNLOADER_MAX_FILENAME_SIZE=128 + -DCONFIG_DOWNLOADER_TRANSPORT_PARAMS_SIZE=128 + -DCONFIG_DOWNLOADER_STACK_SIZE=1024 -DCONFIG_DFU_TARGET_SMP=1 ) diff --git a/tests/subsys/net/lib/nrf_cloud/cloud/CMakeLists.txt b/tests/subsys/net/lib/nrf_cloud/cloud/CMakeLists.txt index 1a62f3825f48..f002e99f74e5 100644 --- a/tests/subsys/net/lib/nrf_cloud/cloud/CMakeLists.txt +++ b/tests/subsys/net/lib/nrf_cloud/cloud/CMakeLists.txt @@ -55,8 +55,8 @@ if (CONFIG_NRF_CLOUD_MQTT OR CONFIG_NRF_CLOUD_FOTA OR CONFIG_NRF_MODEM_LIB) # NET_SOCKETS_POSIX_NAMES=y in zephyr/net/socket.h, so # it needs to be excluded when NET_SOCKETS_POSIX_NAMES=n set_source_files_properties( - ${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/download_client/src/download_client.c - DIRECTORY ${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/download_client/ + ${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/downloader/src/downloader.c + DIRECTORY ${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/downloader/ PROPERTIES HEADER_FILE_ONLY ON )