text: // ----------------------------------------------------------------------------
  // Copyright 2016-2017 ARM Ltd.
  //
  // SPDX-License-Identifier: Apache-2.0
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //     http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  // ----------------------------------------------------------------------------
  
  #include "update-client-common/arm_uc_config.h"
  
  #if defined(ARM_UC_FEATURE_FW_SOURCE_HTTP) && (ARM_UC_FEATURE_FW_SOURCE_HTTP == 1)
  
  // HTTP streaming of downloads.
  // ----------------------------
  // Implements an update source for update client use.
  // Keeps the fragment-get interface for all types, but replaces it under the
  //   hood with pre-fetch and caching of the requested data.
  // The pre-fetch entails requesting the server for the remainder of the resource,
  //   rather than just the fragment mentioned, and allowing the HTTP/TCP stack
  //   to do the work of buffering it. The caching entails keeping track of the
  //   current state of the buffered fetch, and returning the fragment as read
  //   from the buffered stream if appropriate, or re-requesting the data if there
  //   is no match between the request and the cached state, or the socket has
  //   been broken and the buffer is unavailable.
  // Note that streaming is not necessarily always the correct approach, there
  //   might on occasion be cause to force the fragment approach, related to
  //   available transports, or link properties.
  
  #include "update-client-source-http/arm_uc_source_http.h"
  
  #include "update-client-source-http-socket/arm_uc_http_socket.h"
  
  #include <time.h>
  #include <stdio.h>
  #include <stdbool.h>
  #include "update-client-source-http/arm_uc_source_http_extra.h"
  
  // TRACE.
  // ------
  
  // to disable extra trace, uncomment UC_SRCE_TRACE_ENTRY/VERBOSE/EXIT(...)
  // or to enable extra trace, uncomment UC_SRCE_TRACE_ENTRY/VERBOSE/EXIT UC_SRCE_TRACE
  #define UC_SRCE_TRACE_ENTRY(...)
  #define UC_SRCE_TRACE_VERBOSE(...)
  #define UC_SRCE_TRACE_EXIT(...)
  //#define UC_SRCE_TRACE_ENTRY UC_SRCE_TRACE
  //#define UC_SRCE_TRACE_VERBOSE UC_SRCE_TRACE
  //#define UC_SRCE_TRACE_EXIT UC_SRCE_TRACE
  
  // DATA & CONFIG.
  // --------------
  
  // current version of this driver.
  #define DRIVER_VER 0x00010000
  
  // default cost is lowered compared to fragment sources.
  #define ARM_UCS_HTTP_DEFAULT_COST (700)
  #define ARM_UCS_HTTP_HASH_LENGTH  (40)
  
  typedef struct _ARM_UCS_Http_Configuration {
      arm_uc_uri_t manifest;
      uint32_t interval;
      uint32_t currentCost;
      time_t lastPoll;
      int8_t hash[ARM_UCS_HTTP_HASH_LENGTH];
      void (*eventHandler)(uintptr_t event);
  } ARM_UCS_Http_Configuration_t;
  
  static ARM_UCS_Http_Configuration_t default_config = {
      .manifest = {
          .size_max = 0,
          .size = 0,
          .ptr = NULL,
          .scheme = URI_SCHEME_NONE,
          .port = 0,
          .host = NULL,
          .path = NULL
      },
      .interval = 0,
      .currentCost = 0xFFFFFFFF,
      .lastPoll = 0,
      .hash = { 0 },
      .eventHandler = 0
  };
  
  typedef enum {
      STATE_UCS_HTTP_IDLE,
      STATE_UCS_HTTP_MANIFEST,
      STATE_UCS_HTTP_FIRMWARE,
      STATE_UCS_HTTP_FIRMWARE_RELOAD,
      STATE_UCS_HTTP_KEYTABLE,
      STATE_UCS_HTTP_HASH
  } arm_ucs_http_state_t;
  
  #define MAX_RETRY 3
  
  typedef struct {
      arm_ucs_http_state_t stateHttp;
      arm_uc_uri_t *uri;
      arm_uc_buffer_t *buffer;
      uint32_t offset;
      uint8_t retryCount;
  } arm_ucs_state_t;
  
  static arm_ucs_state_t arm_ucs_state;
  
  static arm_uc_http_socket_context_t arm_uc_http_socket_context = { 0 };
  
  // HELPERS.
  // --------
  
  /* Helper function for resetting internal state */
  static inline void uc_state_reset()
  {
      arm_ucs_state.stateHttp  = STATE_UCS_HTTP_IDLE;
      arm_ucs_state.uri        = NULL;
      arm_ucs_state.buffer     = NULL;
      arm_ucs_state.offset     = 0;
      arm_ucs_state.retryCount = 0;
  }
  
  /* Helper function for checking if the stored hash is all zeros */
  static inline bool hash_is_zero()
  {
      bool result = true;
      for (uint32_t index = 0; index < ARM_UCS_HTTP_HASH_LENGTH; index++) {
          if (default_config.hash[index] != 0) {
              result = false;
              break;
          }
      }
      return result;
  }
  
  static arm_uc_error_t arm_ucs_http_error = {ERR_NONE};
  arm_uc_error_t ARM_UCS_Http_GetError(void) { return arm_ucs_http_error; }
  arm_uc_error_t ARM_UCS_Http_SetError(arm_uc_error_t an_error) { return (arm_ucs_http_error = an_error); }
  
  
  // FORWARD DECLARATIONS.
  // ---------------------
  
  arm_uc_error_t ARM_UCS_Http_Get(arm_uc_uri_t *uri,
                                  arm_uc_buffer_t *buffer,
                                  uint32_t offset,
                                  arm_ucs_http_state_t newHttpState);
  
  /******************************************************************************/
  /* ARM Update Client Source Extra                                             */
  /******************************************************************************/
  
  /**
   * @brief Set URI location for the default manifest.
   * @details The default manifest is polled regularly and generates a
   *          notification upon change. The URI struct and the content pointer to
   *          must be valid throughout the lifetime of the application.
   *
   * @param uri URI struct with manifest location.
   * @return Error code.
   */
  arm_uc_error_t ARM_UCS_Http_SetDefaultManifestURL(arm_uc_uri_t *uri)
  {
      UC_SRCE_TRACE_ENTRY(">> %s", __func__);
  
      /* check scheme is http */
      if ((uri == NULL) || (uri->scheme != URI_SCHEME_HTTP)) {
          ARM_UCS_Http_SetError((arm_uc_error_t) {SRCE_ERR_INVALID_PARAMETER});
          return ARM_UC_ERROR(SRCE_ERR_INVALID_PARAMETER);
      } else {
          /* copy pointers to local struct */
          default_config.manifest = *uri;
          return ARM_UC_ERROR(ERR_NONE);
      }
  }
  
  /**
   * @brief Set polling interval for notification generation.
   * @details The default manifest location is polled with this interval.
   *
   * @param seconds Seconds between each poll.
   * @return Error code.
   */
  arm_uc_error_t ARM_UCS_Http_SetPollingInterval(uint32_t seconds)
  {
      UC_SRCE_TRACE_ENTRY(">> %s", __func__);
  
      default_config.interval = seconds;
  
      return ARM_UC_ERROR(ERR_NONE);
  }
  
  /**
   * @brief Main function for the Source.
   * @details This function will query the default manifest location and generate
   *          a notification if it has changed since the last time it was checked.
   *          The number of queries generated is bound by the polling interval.
   *
   *          This function should be used on systems with timed callbacks.
   *
   * @return Seconds until the next polling interval.
   */
  uint32_t ARM_UCS_Http_CallMultipleTimes(arm_uc_buffer_t *hash_buffer)
  {
      UC_SRCE_TRACE_ENTRY(">> %s", __func__);
  
      uint32_t result = default_config.interval;
      time_t unixtime = time(NULL);
      uint32_t elapsed = unixtime - default_config.lastPoll;
  
      if ((default_config.eventHandler == NULL) ||
              (arm_ucs_state.stateHttp != STATE_UCS_HTTP_IDLE) ||
              (hash_buffer == NULL)) {
          return default_config.interval;
      }
  
      if (elapsed >= default_config.interval) {
          UC_SRCE_TRACE_VERBOSE("%s interval elapsed", __func__);
  
          // poll default URI
          default_config.lastPoll = unixtime;
  
          // get resource hash
          arm_ucs_state.stateHttp = STATE_UCS_HTTP_HASH;
          arm_ucs_state.buffer = hash_buffer;
  
          arm_uc_error_t retval = ARM_UCS_HttpSocket_GetHash(&default_config.manifest,
                                                             arm_ucs_state.buffer);
          if (ARM_UC_IS_ERROR(retval)) {
              uc_state_reset();
              return default_config.interval;
          }
      } else {
          result = (elapsed > 0) ? default_config.interval - elapsed : default_config.interval;
      }
  
      return result;
  }
  
  // EXTRA INTERFACE.
  // ----------------
  
  ARM_UCS_HTTPSourceExtra_t ARM_UCS_HTTPSourceExtra = {
      .SetDefaultManifestURL = ARM_UCS_Http_SetDefaultManifestURL,
      .SetPollingInterval    = ARM_UCS_Http_SetPollingInterval,
      .CallMultipleTimes     = ARM_UCS_Http_CallMultipleTimes
  };
  
  /******************************************************************************/
  /* ARM Update Client Source                                                   */
  /******************************************************************************/
  
  void ARM_UCS_Http_ProcessHash()
  {
      UC_SRCE_TRACE_ENTRY(">> %s", __func__);
  
  #if ARM_UC_SOURCE_MANAGER_TRACE_ENABLE
      printf("hash: ");
      for (uint32_t index = 0; index < arm_ucs_state.buffer->size; index++) {
          printf("%c", arm_ucs_state.buffer->ptr[index]);
      }
      printf("\r\n");
  #endif
  
      bool hashIsNew = false;
      bool firstBoot = hash_is_zero();
  
      /* compare hash with previous check */
      for (uint32_t index = 0; index < arm_ucs_state.buffer->size; index++) {
          /* compare hash */
          if (default_config.hash[index] != arm_ucs_state.buffer->ptr[index]) {
              /* store new hash */
              default_config.hash[index] = arm_ucs_state.buffer->ptr[index];
              hashIsNew = true;
          }
      }
  
      /* Request complete, reset state */
      uc_state_reset();
      ARM_UC_HttpSocket_EndResume();
  
      /* Signal that a new manifest is available if the hash is non-zero
         and different from the last check.
      */
      if (hashIsNew && !firstBoot) {
          if (default_config.eventHandler) {
              default_config.eventHandler(EVENT_NOTIFICATION);
          }
      }
  }
  
  static void ARM_UCS_Http_HTTPEvent(uintptr_t event)
  {
      UC_SRCE_TRACE_ENTRY(">> %s", __func__);
  
      switch (event) {
          /* Hash received, process it */
          case UCS_HTTP_EVENT_HASH:
              UC_SRCE_TRACE("UCS_HTTP_EVENT_HASH");
  
              ARM_UCS_Http_ProcessHash();
              break;
  
          /* Download complete */
          case UCS_HTTP_EVENT_DOWNLOAD: {
              UC_SRCE_TRACE("UCS_HTTP_EVENT_DOWNLOAD");
  
              /* cache state before resetting it */
              arm_ucs_http_state_t previous_state = arm_ucs_state.stateHttp;
  
              /* reset internal state */
              uc_state_reset();
  
              /* signal successful download based on request */
              if (default_config.eventHandler) {
                  if (previous_state == STATE_UCS_HTTP_MANIFEST) {
                      default_config.eventHandler(EVENT_MANIFEST);
                  } else if (previous_state == STATE_UCS_HTTP_FIRMWARE) {
                      default_config.eventHandler(EVENT_FIRMWARE);
                  } else if (previous_state == STATE_UCS_HTTP_KEYTABLE) {
                      default_config.eventHandler(EVENT_KEYTABLE);
                  } else {
                      default_config.eventHandler(EVENT_ERROR);
                  }
              }
          }
          break;
  
          /* Socket error */
          case UCS_HTTP_EVENT_ERROR: {
              UC_SRCE_TRACE("UCS_HTTP_EVENT_ERROR");
  
              /* Treat polling as retry when reading hash */
              if (arm_ucs_state.stateHttp == STATE_UCS_HTTP_HASH) {
                  /* If the stored hash is zero, this error is most likely
                     generated due to the default manifest not being uploaded
                     yet. Mark the stored hash as non-zero, so the first time
                     the device successfully retrieves a hash we download the
                     manifest.
                  */
                  if (hash_is_zero()) {
                      default_config.hash[0] = 0xFF;
                  }
                  /* reset state but don't take any further action */
                  uc_state_reset();
              } else {
                  /* reset internal state */
                  uc_state_reset();
  
                  /* generate error event */
                  if (default_config.eventHandler) {
                      default_config.eventHandler(EVENT_ERROR_SOURCE);
                  }
              }
          }
          break;
  
          /* supplied buffer not large enough */
          case UCS_HTTP_EVENT_ERROR_BUFFER_SIZE: {
              /* reset internal state */
              uc_state_reset();
  
              /* generate error event */
              if (default_config.eventHandler) {
                  default_config.eventHandler(EVENT_ERROR_BUFFER_SIZE);
              }
          }
          break;
  
          default:
              UC_SRCE_ERR_MSG("%s Unknown event", __func__);
              break;
      }
  }
  
  /**
   * @brief Get driver version.
   * @return Driver version.
   */
  uint32_t ARM_UCS_Http_GetVersion(void)
  {
      UC_SRCE_TRACE_ENTRY(">> %s", __func__);
      return DRIVER_VER;
  }
  
  /**
   * @brief Get Source capabilities.
   * @return Struct containing capabilites. See definition above.
   */
  ARM_SOURCE_CAPABILITIES ARM_UCS_Http_GetCapabilities(void)
  {
      UC_SRCE_TRACE_ENTRY(">> %s", __func__);
  
      ARM_SOURCE_CAPABILITIES result = {
          .notify = 0,
          .manifest_default = 0,
          .manifest_url = 0,
          .firmware = 0,
          .keytable = 0
      };
  
      /* the event handler must be set before module can be used */
      if (default_config.eventHandler != 0) {
          result.manifest_url = 1;
          result.firmware = 1;
          result.keytable = 1;
  
          /* notification requires that the default manifest is set */
          if ((default_config.manifest.port != 0) || (default_config.interval != 0)) {
              result.notify = 1;
              result.manifest_default = 1;
          }
      }
  
      return result;
  }
  
  /**
   * @brief Initialize Source.
   * @details Function pointer to event handler is passed as argument.
   *
   * @param cb_event Function pointer to event handler. See events above.
   * @return Error code.
   */
  arm_uc_error_t ARM_UCS_Http_Initialize(ARM_SOURCE_SignalEvent_t cb_event)
  {
      UC_SRCE_TRACE_ENTRY(">> %s", __func__);
      ARM_UC_INIT_ERROR(status, SRCE_ERR_INVALID_PARAMETER);
  
      if (cb_event != NULL) {
          default_config.currentCost = ARM_UCS_HTTP_DEFAULT_COST;
          default_config.eventHandler = cb_event;
  
          /* register http callback handler */
          ARM_UCS_HttpSocket_Initialize(
              &arm_uc_http_socket_context,
              ARM_UCS_Http_HTTPEvent);
  
          ARM_UC_SET_ERROR(status, ERR_NONE);
      }
      uc_state_reset();
      if (ARM_UC_IS_ERROR(status)) {
          ARM_UCS_Http_SetError(status);
      }
      return status;
  }
  
  /**
   * @brief Uninitialized Source.
   * @return Error code.
   */
  arm_uc_error_t ARM_UCS_Http_Uninitialize(void)
  {
      UC_SRCE_TRACE_ENTRY(">> %s", __func__);
      ARM_UCS_Http_SetError((arm_uc_error_t) {SRCE_ERR_INVALID_PARAMETER});
      return ARM_UC_ERROR(SRCE_ERR_INVALID_PARAMETER);
  }
  
  // COSTS.
  // ------
  
  /**
   * @brief Cost estimation for retrieving manifest from the default location.
   * @details The estimation can vary over time and should not be cached too long.
   *          0x00000000 - The manifest is already downloaded.
   *          0xFFFFFFFF - Cannot retrieve manifest from this Source.
   *
   * @param cost Pointer to variable for the return value.
   * @return Error code.
   */
  arm_uc_error_t ARM_UCS_Http_GetManifestDefaultCost(
      uint32_t *a_cost_p)
  {
      UC_SRCE_TRACE_ENTRY(">> %s", __func__);
      ARM_UC_INIT_ERROR(status, SRCE_ERR_INVALID_PARAMETER);
  
      if (a_cost_p != 0) {
          *a_cost_p = default_config.currentCost;
          ARM_UC_SET_ERROR(status, ERR_NONE);
      }
      if (ARM_UC_IS_ERROR(status)) {
          ARM_UCS_Http_SetError(status);
      }
      return status;
  }
  
  /**
   * @brief Cost estimation for retrieving unspecified resource from URL.
   * @details The estimation can vary over time and should not be cached too long.
   *          0x00000000 - The manifest is already downloaded.
   *          0xFFFFFFFF - Cannot retrieve manifest from this Source.
   * @param uri URI struct with manifest location.
   * @param cost Pointer to variable for the return value.
   * @return Error code.
   */
  arm_uc_error_t ARM_UCS_Http_GetCost(
      arm_uc_uri_t *a_uri_p,
      uint32_t *a_cost_p)
  {
      UC_SRCE_TRACE_ENTRY(">> %s", __func__);
      ARM_UC_INIT_ERROR(status, SRCE_ERR_INVALID_PARAMETER);
  
      /* return default cost regardless of actual uri location */
      if ((a_uri_p != NULL) && (a_cost_p != NULL)) {
          *a_cost_p = default_config.currentCost;
          ARM_UC_SET_ERROR(status, ERR_NONE);
      }
      /* return no-path cost if URL is invalid */
      else if (a_cost_p != NULL) {
          *a_cost_p = 0xFFFFFFFF;
      }
      if (ARM_UC_IS_ERROR(status)) {
          ARM_UCS_Http_SetError(status);
      }
      return status;
  }
  
  /* @brief Cost estimation for retrieving manifest from URL.
   * @details The estimation can vary over time and should not be cached too long.
   *          0x00000000 - The manifest is already downloaded.
   *          0xFFFFFFFF - Cannot retrieve manifest from this Source.
   *
   * @param uri URI struct with manifest location.
   * @param cost Pointer to variable for the return value.
   * @return Error code.
   */
  arm_uc_error_t ARM_UCS_Http_GetManifestURLCost(arm_uc_uri_t *uri, uint32_t *cost)
  {
      UC_SRCE_TRACE_ENTRY(">> %s", __func__);
      return ARM_UCS_Http_GetCost(uri, cost);
  }
  
  /**
   * @brief Cost estimation for retrieving firmware from URL.
   * @details The estimation can vary over time and should not be cached too long.
   *          0x00000000 - The firmware is already downloaded.
   *          0xFFFFFFFF - Cannot retrieve firmware from this Source.
   *
   * @param uri URI struct with firmware location.
   * @param cost Pointer to variable for the return value.
   * @return Error code.
   */
  arm_uc_error_t ARM_UCS_Http_GetFirmwareURLCost(arm_uc_uri_t *uri, uint32_t *cost)
  {
      UC_SRCE_TRACE_ENTRY(">> %s", __func__);
      return ARM_UCS_Http_GetCost(uri, cost);
  }
  
  /**
   * @brief Cost estimation for retrieving key table from URL.
   * @details The estimation can vary over time and should not be cached too long.
   *          0x00000000 - The firmware is already downloaded.
   *          0xFFFFFFFF - Cannot retrieve firmware from this Source.
   *
   * @param uri URI struct with keytable location.
   * @param cost Pointer to variable for the return value.
   * @return Error code.
   */
  arm_uc_error_t ARM_UCS_Http_GetKeytableURLCost(arm_uc_uri_t *uri, uint32_t *cost)
  {
      UC_SRCE_TRACE_ENTRY(">> %s", __func__);
      return ARM_UCS_Http_GetCost(uri, cost);
  }
  
  // GETTING RESOURCES.
  // ------------------
  
  /**
   * @brief (Internal) Retrieve resource according to the given parameters
   *        in arm_ucs_state and store the returned data in the buffer in arm_ucs_state.
   * @param uri URI struct with resource location.
   * @param buffer Struct containing byte array, maximum size, and actual size.
   * @param offset Offset to retrieve fragment from.
   * @param newHttpState The intended new http state to be assigned to arm_ucs_state.stateHttp
   */
  
  arm_uc_error_t ARM_UCS_Http_Get(arm_uc_uri_t *uri,
                                  arm_uc_buffer_t *buffer,
                                  uint32_t offset,
                                  arm_ucs_http_state_t newHttpState)
  {
      UC_SRCE_TRACE_ENTRY(">> %s", __func__);
      ARM_UC_INIT_ERROR(status, SRCE_ERR_INVALID_PARAMETER);
  
      // Call the socket layer to get the requested data
  
      // check current state
      if (default_config.eventHandler == 0) {
          UC_SRCE_ERR_MSG("Uninitialized");
          return ARM_UC_ERROR(SRCE_ERR_UNINITIALIZED);
      }
      if (arm_ucs_state.stateHttp != STATE_UCS_HTTP_IDLE) {
          UC_SRCE_ERR_MSG("Busy");
          return ARM_UC_ERROR(SRCE_ERR_BUSY);
      }
  
      // assign new state
      arm_ucs_state.stateHttp      = newHttpState;
      arm_ucs_state.uri            = uri;
      arm_ucs_state.buffer         = buffer;
      arm_ucs_state.offset         = offset;
  
      // never returns an error because all Get does is installs the thread.
      while (ARM_UC_IS_ERROR(status) && (arm_ucs_state.retryCount++ < MAX_RETRY)) {
          // restore buffer size on retry
          arm_ucs_state.buffer->size = arm_ucs_state.buffer->size_max;
  
          switch (arm_ucs_state.stateHttp) {
              case STATE_UCS_HTTP_MANIFEST:
              case STATE_UCS_HTTP_FIRMWARE:
                  if (arm_ucs_state.buffer != 0 && arm_ucs_state.uri != 0) {
                      status = ARM_UCS_HttpSocket_GetFragment(arm_ucs_state.uri,
                                                              arm_ucs_state.buffer,
                                                              arm_ucs_state.offset);
                  }
                  break;
              case STATE_UCS_HTTP_KEYTABLE:
                  if (arm_ucs_state.buffer != 0 && arm_ucs_state.uri != 0) {
                      status = ARM_UCS_HttpSocket_GetFile(arm_ucs_state.uri,
                                                          arm_ucs_state.buffer);
                  }
                  break;
              default:
                  UC_SRCE_ERR_MSG("Invalid request parameter");
                  ARM_UC_SET_ERROR(status, SRCE_ERR_INVALID_PARAMETER);
                  break;
          }
      }
  
      if (ARM_UC_IS_ERROR(status)) {
          uc_state_reset();
      }
      if (ARM_UC_IS_ERROR(status)) {
          ARM_UCS_Http_SetError(status);
      }
      return status;
  }
  
  /**
   * @brief Retrieve manifest from the default location.
   * @details Manifest is stored in supplied buffer.
   *          Event is generated once manifest is in buffer.
   *
   * @param buffer Struct containing byte array, maximum size, and actual size.
   * @return Error code.
   */
  arm_uc_error_t ARM_UCS_Http_GetManifestDefault(arm_uc_buffer_t *buffer, uint32_t offset)
  {
      UC_SRCE_TRACE_ENTRY(">> %s", __func__);
      return ARM_UCS_Http_Get(&default_config.manifest, buffer, offset, STATE_UCS_HTTP_MANIFEST);
  }
  
  /**
   * @brief Retrieve manifest from URL.
   * @details Manifest is stored in supplied buffer.
   *          Event is generated once manifest is in buffer.
   *
   * @param uri URI struct with manifest location.
   * @param buffer Struct containing byte array, maximum size, and actual size.
   *
   * @return Error code.
   */
  arm_uc_error_t ARM_UCS_Http_GetManifestURL(arm_uc_uri_t *uri, arm_uc_buffer_t *buffer, uint32_t offset)
  {
      UC_SRCE_TRACE_ENTRY(">> %s", __func__);
      return ARM_UCS_Http_Get(uri, buffer, offset, STATE_UCS_HTTP_MANIFEST);
  }
  
  /**
   * @brief Retrieve firmware fragment.
   * @details Firmware fragment is stored in supplied buffer.
   *          Event is generated once fragment is in buffer.
   *
   * @param uri URI struct with firmware location.
   * @param buffer Struct containing byte array, maximum size, and actual size.
   * @param offset Firmware offset to retrieve fragment from.
   * @return Error code.
   */
  arm_uc_error_t ARM_UCS_Http_GetFirmwareFragment(arm_uc_uri_t *uri, arm_uc_buffer_t *buffer, uint32_t offset)
  {
      UC_SRCE_TRACE_ENTRY(">> %s", __func__);
      return ARM_UCS_Http_Get(uri, buffer, offset, STATE_UCS_HTTP_FIRMWARE);
  }
  
  /**
   * @brief Retrieve a key table from a URL.
   * @details Key table is stored in supplied buffer.
   *          Event is generated once fragment is in buffer.
   *
   * @param uri URI struct with keytable location.
   * @param buffer Struct containing byte array, maximum size, and actual size.
   * @return Error code.
   */
  arm_uc_error_t ARM_UCS_Http_GetKeytableURL(arm_uc_uri_t *uri, arm_uc_buffer_t *buffer)
  {
      UC_SRCE_TRACE_ENTRY(">> %s", __func__);
      return ARM_UCS_Http_Get(uri, buffer, UINT32_MAX, STATE_UCS_HTTP_KEYTABLE);
  }
  
  // SOURCE INTERFACE.
  // -----------------
  
  ARM_UPDATE_SOURCE ARM_UCS_HTTPSource = {
      .GetVersion             = ARM_UCS_Http_GetVersion,
      .GetCapabilities        = ARM_UCS_Http_GetCapabilities,
      .Initialize             = ARM_UCS_Http_Initialize,
      .Uninitialize           = ARM_UCS_Http_Uninitialize,
      .GetManifestDefaultCost = ARM_UCS_Http_GetManifestDefaultCost,
      .GetManifestURLCost     = ARM_UCS_Http_GetManifestURLCost,
      .GetFirmwareURLCost     = ARM_UCS_Http_GetFirmwareURLCost,
      .GetKeytableURLCost     = ARM_UCS_Http_GetKeytableURLCost,
      .GetManifestDefault     = ARM_UCS_Http_GetManifestDefault,
      .GetManifestURL         = ARM_UCS_Http_GetManifestURL,
      .GetFirmwareFragment    = ARM_UCS_Http_GetFirmwareFragment,
      .GetKeytableURL         = ARM_UCS_Http_GetKeytableURL
  };
  
  #endif // ARM_UC_FEATURE_FW_SOURCE_HTTP
  
  