leo hendrickson / Mbed OS example-Ethernet-mbed-Cloud-connect
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers arm_uc_http_socket_private.c Source File

arm_uc_http_socket_private.c

00001 // ----------------------------------------------------------------------------
00002 // Copyright 2016-2017 ARM Ltd.
00003 //
00004 // SPDX-License-Identifier: Apache-2.0
00005 //
00006 // Licensed under the Apache License, Version 2.0 (the "License");
00007 // you may not use this file except in compliance with the License.
00008 // You may obtain a copy of the License at
00009 //
00010 //     http://www.apache.org/licenses/LICENSE-2.0
00011 //
00012 // Unless required by applicable law or agreed to in writing, software
00013 // distributed under the License is distributed on an "AS IS" BASIS,
00014 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015 // See the License for the specific language governing permissions and
00016 // limitations under the License.
00017 // ----------------------------------------------------------------------------
00018 
00019 #include "update-client-common/arm_uc_config.h"
00020 
00021 #if defined(ARM_UC_FEATURE_FW_SOURCE_HTTP) && (ARM_UC_FEATURE_FW_SOURCE_HTTP == 1)
00022 
00023 /**
00024  * This file implements a streaming update source over HTTP.
00025  * See arm_uc_http.c for more info on the pre-fetch and caching.
00026  * The control of caching functionality resides largely in the open_* fields
00027  *   of the 'context' structure being used to manage the download.
00028  */
00029 
00030 // If enabled, text messages will be printed to output to give live feedback for QA testing.
00031 // This is intended for QA testing ***only***, and should not be enabled for any other reason.
00032 #if defined(ARM_UC_HTTP_RESUME_TEST_MESSAGES_ENABLE) && (ARM_UC_HTTP_RESUME_TEST_MESSAGES_ENABLE == 1)
00033 #define ARM_UC_QA_TRACE_ENABLE 1
00034 #endif
00035 
00036 #include "update-client-resume-engine/arm_uc_resume.h"
00037 #include "arm_uc_http_socket_private.h"
00038 #include <pal.h>
00039 #include <stdio.h>
00040 #include <string.h>
00041 #include <inttypes.h>
00042 #include "arm_uc_socket_help.h"
00043 
00044 // TRACE.
00045 // ------
00046 
00047 // to disable extra trace, uncomment UC_SRCE_TRACE_ENTRY/VERBOSE/EXIT(...)
00048 // or to enable extra trace, uncomment UC_SRCE_TRACE_ENTRY/VERBOSE/EXIT UC_SRCE_TRACE
00049 #define UC_SRCE_TRACE_ENTRY(...)
00050 #define UC_SRCE_TRACE_VERBOSE(...)
00051 #define UC_SRCE_TRACE_EXIT(...)
00052 //#define UC_SRCE_TRACE_ENTRY UC_SRCE_TRACE
00053 //#define UC_SRCE_TRACE_VERBOSE UC_SRCE_TRACE
00054 //#define UC_SRCE_TRACE_EXIT UC_SRCE_TRACE
00055 
00056 // FORWARD DECLARATIONS.
00057 // ---------------------
00058 
00059 #define ISR_EVENT_MASK (0x80)
00060 
00061 
00062 
00063 static void arm_uc_http_socket_isr(
00064     void *an_event_origin);
00065 
00066 static arm_uc_error_t arm_uc_http_install_isr_event(
00067     arm_uc_http_socket_event_t an_event);
00068 
00069 static arm_uc_error_t arm_uc_http_install_app_event(
00070     arm_uc_http_socket_event_t an_event);
00071 
00072 
00073 // DATA & CONFIG.
00074 // --------------
00075 
00076 /* Pointer to struct containing all global variables.
00077  * Can be dynamically allocated and deallocated.
00078  */
00079 static arm_uc_http_socket_context_t *context = NULL;
00080 
00081 /* Number of frags to be requested per GET request (burst) */
00082 uint32_t frags_per_burst = ARM_UC_MULTI_FRAGS_PER_HTTP_BURST;
00083 
00084 // This fills in the values from the header if specified non-default.
00085 
00086 // Exponentiation factor tries to balance speed with power considerations.
00087 // Resume is very aggressive to start but backs off more quickly too.
00088 #if !defined(ARM_UC_HTTP_RESUME_EXPONENTIATION_FACTOR)
00089 #define ARM_UC_HTTP_RESUME_EXPONENTIATION_FACTOR (ARM_UC_HTTP_RESUME_DEFAULT_EXPONENTIATION_FACTOR)
00090 #elif ARM_UC_HTTP_RESUME_EXPONENTIATION_FACTOR < 0
00091 #error "HTTP resume attempt delay exponentiation factor must be non-negative."
00092 #endif
00093 
00094 // Delay parameters have minimum and maximum values.
00095 // In general the minimum is a hard limit, because going too low will interfere with the algorithm,
00096 //   given that there are various phases which need to coordinate.
00097 // The maximum delays have no hard limits, but issue a warning if they seem unreasonably long,
00098 //   which is intended to catch errors like extra zeroes in the #defined values.
00099 
00100 #define MIN_INITIAL_ATTEMPT_DELAY_LIMIT         (1*1000)
00101 #define ADVISABLE_INITIAL_ATTEMPT_DELAY_LIMIT   (60*60*1000)
00102 #define MAX_INITIAL_ATTEMPT_DELAY_LIMIT         (24*60*60*1000UL)
00103 
00104 #define ADVISABLE_LONGEST_ATTEMPT_DELAY_LIMIT   (24*60*60*1000UL)
00105 #define MAX_LONGEST_ATTEMPT_DELAY_LIMIT         (7*24*60*60*1000UL)
00106 
00107 #define ADVISABLE_ACTIVITY_TIME_LIMIT           (30*24*60*60*1000UL)
00108 #define MAX_ACTIVITY_TIME_LIMIT                 (30*24*60*60*1000UL)
00109 
00110 // Initial delay between resumption attempts.
00111 // Default and lower bound is 1 seconds, maximum is 1 hour.
00112 // Because we now don't have retries at the source level, we cover that fault profile with resume/8.
00113 #if !defined(ARM_UC_HTTP_RESUME_INITIAL_DELAY_SECS)
00114 #define ARM_UC_HTTP_RESUME_INITIAL_DELAY_SECS   (ARM_UC_HTTP_RESUME_DEFAULT_INITIAL_DELAY_SECS)
00115 #elif ARM_UC_HTTP_RESUME_INITIAL_DELAY_SECS < 0
00116 #error "HTTP resume initial attempt delay must be non-negative."
00117 #endif
00118 #define ARM_UC_HTTP_RESUME_INITIAL_DELAY_MSECS  ((ARM_UC_HTTP_RESUME_INITIAL_DELAY_SECS)*1000UL)
00119 
00120 // Greatest delay between resumption attempts.
00121 // Default to 1 hour, lower bound is minimum delay, maximum 7 day.
00122 #if !defined(ARM_UC_HTTP_RESUME_MAXIMUM_DELAY_SECS)
00123 #define ARM_UC_HTTP_RESUME_MAXIMUM_DELAY_SECS   (ARM_UC_HTTP_RESUME_DEFAULT_MAXIMUM_DELAY_SECS)
00124 #elif ARM_UC_HTTP_RESUME_MAXIMUM_DELAY_SECS < 0
00125 #error "HTTP resume maximum attempt delay must be non-negative."
00126 #endif
00127 #define ARM_UC_HTTP_RESUME_MAXIMUM_DELAY_MSECS         ((ARM_UC_HTTP_RESUME_MAXIMUM_DELAY_SECS)*1000UL)
00128 
00129 // Stop resumptions after this period has elapsed.
00130 // Default to 24 hours, lower bound is maximum delay, maximum is 30 days.
00131 #if !defined(ARM_UC_HTTP_RESUME_MAXIMUM_DOWNLOAD_TIME_SECS)
00132 #define ARM_UC_HTTP_RESUME_MAXIMUM_DOWNLOAD_TIME_SECS (ARM_UC_HTTP_RESUME_DEFAULT_MAXIMUM_DOWNLOAD_TIME_SECS)
00133 #elif ARM_UC_HTTP_RESUME_MAXIMUM_DOWNLOAD_TIME_SECS < 0
00134 #error "HTTP resume maximum download time must be non-negative"
00135 #endif
00136 #define ARM_UC_HTTP_RESUME_MAXIMUM_DOWNLOAD_TIME_MSECS ((ARM_UC_HTTP_RESUME_MAXIMUM_DOWNLOAD_TIME_SECS)*1000UL)
00137 
00138 // These values are not user-configurable because they are largely dependent on the resume settings,
00139 //   inasmuch as the expected behaviour of the link implies the interval behaviour.
00140 
00141 // The number of attempts to push the state machine along, assuming events have been dropped,
00142 //   and the periods of time between the intervals.
00143 #define ARM_UC_HTTP_RESUME_NUM_INTERVALS           2
00144 
00145 // This is the period of time before a socket is timed out as being behind,
00146 //   but not necessarily in error, given that events can go missing over the network.
00147 //   Instead we use this to optimistically bump the socket along, in the expectation that it will
00148 //   perhaps have had an event but not reported it, unlike what we would have expected in a perfect world.
00149 #define ARM_UC_HTTP_RESUME_INTERVAL_DELAY_MSECS\
00150         (ARM_UC_HTTP_RESUME_INITIAL_DELAY_MSECS/(ARM_UC_HTTP_RESUME_NUM_INTERVALS+1))
00151 
00152 // Runtime storage of current status of HTTP resumptions.
00153 arm_uc_resume_t resume_http;
00154 
00155 static void on_http_resume_interval(void *a_context_p)
00156 {
00157     if (resume_http.num_intervals > resume_http.interval_count) {
00158         arm_uc_http_install_isr_event((arm_uc_http_socket_event_t) SOCKET_EVENT_RESUME_WAITING);
00159     } else {
00160         arm_uc_http_install_isr_event((arm_uc_http_socket_event_t) SOCKET_EVENT_RESUME_INTERVAL);
00161     }
00162 }
00163 static void on_http_resume_attempt(void *a_context_p)
00164 {
00165     arm_uc_http_install_isr_event((arm_uc_http_socket_event_t) SOCKET_EVENT_RESUME_ATTEMPT);
00166 }
00167 static void on_http_resume_terminate(void *a_context_p)
00168 {
00169     arm_uc_http_install_isr_event((arm_uc_http_socket_event_t) SOCKET_EVENT_RESUME_TERMINATED);
00170 }
00171 // Called from interrupt context!
00172 // We can't afford to do much here, so just set a flag for later handling.
00173 // This will be trapped by the socket event handler at some point, which is better than nothing.
00174 static void on_http_resume_error(void *a_context_p)
00175 {
00176 }
00177 
00178 // EVENT MANAGEMENT.
00179 // -----------------
00180 
00181 static uint32_t skip_to_event = 0;
00182 static bool expecting_dns_callback = false;
00183 
00184 /**
00185  * @brief Avoid cycling through the handler queue for trivially consecutive events.
00186  * @details Only used for event transitions that are fast and statically predetermined.
00187  * @param an_event The event to act on in the next cycle.
00188  * @return Error status.
00189  */
00190 arm_uc_error_t arm_uc_http_prepare_skip_to_event(uint32_t an_event)
00191 {
00192     skip_to_event = an_event;
00193     return ARM_UC_ERROR(ERR_NONE);
00194 }
00195 
00196 // SOCKET MANAGEMENT.
00197 // ------------------
00198 
00199 // DNS RESOLUTION.
00200 // ---------------
00201 
00202 /**
00203  * These functions are used to implement DNS resolution in two variants:
00204  * asynchronous (PAL_DNS_API_VERSION is 1 or 2) or
00205  *   synchronous (with PAL_DNS_API_VERSION 0).
00206  * In addition, there is a distinction in that Linux does not currently support v2 API.
00207  */
00208 #if (PAL_DNS_API_VERSION > 1) && !defined(TARGET_LIKE_MBED)
00209 #error "Async PAL DNS API v2 or greater is only supported on Mbed."
00210 #endif
00211 #if (PAL_DNS_API_VERSION == 1) && defined(TARGET_LIKE_MBED)
00212 #error "Async PAL DNS API v1 is not supported on Mbed."
00213 #endif
00214 
00215 /**
00216  * @brief Check if the DNS has already been looked up and saved.
00217  * @return Whether or not it has been cached.
00218  */
00219 bool arm_uc_dns_lookup_is_cached(void)
00220 {
00221     if (context == NULL) {
00222         return false;
00223     } else {
00224         return (context->cache_address.addressType != 0) && (context->cache_address_length != 0);
00225     }
00226 }
00227 
00228 #if (PAL_DNS_API_VERSION == 2)
00229 // Mbed supports the v2 async API.
00230 void arm_uc_dns_callback_handler(
00231     const char *url,
00232     palSocketAddress_t *address,
00233     palStatus_t pal_status,
00234     void *argument);
00235 #else
00236 // Use the v1 async API.
00237 void arm_uc_dns_callback_handler(
00238     const char *url,
00239     palSocketAddress_t *address,
00240     palSocketLength_t *address_length,
00241     palStatus_t pal_status,
00242     void *argument);
00243 #endif
00244 
00245 #if (PAL_DNS_API_VERSION == 2)
00246 // To cancel ongoing asynchronous DNS query with pal_cancelAddressInfoAsync().
00247 palDNSQuery_t arm_uc_dns_query_handle = 0;
00248 
00249 static palStatus_t arm_uc_do_pal_dns_lookup(void)
00250 {
00251     UC_SRCE_TRACE(">> %s [asynchronous v2])", __func__);
00252     // (hack) length is constant.
00253     context->cache_address_length = PAL_NET_MAX_ADDR_SIZE;
00254     palStatus_t result = pal_getAddressInfoAsync(
00255                              context->request_uri->host,
00256                              &context->cache_address,
00257                              arm_uc_dns_callback_handler,
00258                              NULL,
00259                              &arm_uc_dns_query_handle);
00260     UC_SRCE_TRACE(".. %s handle=%" PRIu32 ", result=%" PRIx32,
00261                        __func__, arm_uc_dns_query_handle, result);
00262     return result;
00263 }
00264 #elif (PAL_DNS_API_VERSION == 1)
00265 // Only used internally from a single location, no check for context == NULL.
00266 static palStatus_t arm_uc_do_pal_dns_lookup(void)
00267 {
00268     UC_SRCE_TRACE_ENTRY(">> %s [asynchronous v1])", __func__);
00269     palStatus_t result = pal_getAddressInfoAsync(
00270                              context->request_uri->host,
00271                              &context->cache_address,
00272                              &context->cache_address_length,
00273                              arm_uc_dns_callback_handler,
00274                              NULL);
00275     UC_SRCE_TRACE_EXIT(".. %s result=%" PRIx32, __func__, result);
00276     return result;
00277 }
00278 #elif (PAL_DNS_API_VERSION == 0)
00279 /* Use synchronous DNS calls */
00280 
00281 // Only used internally from a single location, no check for context == NULL.
00282 static palStatus_t arm_uc_do_pal_dns_lookup(void)
00283 {
00284     /* Run the synchronous DNS request and call the callback
00285      immediately after the request is done */
00286     UC_SRCE_TRACE(">> %s [synchronous])", __func__);
00287     palStatus_t status = pal_getAddressInfo(
00288                              context->request_uri->host,
00289                              &context->cache_address,
00290                              &context->cache_address_length);
00291     /* Call the callback with the result of arm_uc_do_dns_lookup.
00292      The callback will examine the status return and act
00293      accordingly (see arm_uc_dns_callback). */
00294     arm_uc_dns_callback_handler(
00295         context->request_uri->host,
00296         &context->cache_address,
00297         &context->cache_address_length,
00298         status,
00299         NULL);
00300     UC_SRCE_TRACE(".. %s result=%" PRIx32, __func__, status);
00301     return status;
00302 }
00303 #endif // PAL_DNS_API_VERSION
00304 
00305 static arm_uc_error_t arm_uc_http_get_address_info(void)
00306 {
00307     UC_SRCE_TRACE_ENTRY(">> %s", __func__);
00308     if (context == NULL) {
00309         UC_SRCE_ERR_MSG("error: context == NULL, &context = %" PRIxPTR, (uintptr_t)context);
00310         ARM_UCS_Http_SetError(SRCE_ERR_UNINITIALIZED);
00311         return ARM_UC_ERROR(SRCE_ERR_UNINITIALIZED);
00312     } else {
00313         UC_SRCE_TRACE("Starting PAL DNS lookup");
00314         expecting_dns_callback = true;
00315         palStatus_t status = arm_uc_do_pal_dns_lookup();
00316 #if (PAL_DNS_API_VERSION == 0)
00317         UC_SRCE_TRACE(">> %s, PAL DNS lookup returned", __func__);
00318         uint64_t data = 0;
00319         int index = 0;
00320         for (data = 0, index = 0; index < 8; ++index) {
00321             data = (data << 8) + context->cache_address.addressData[index];
00322         }
00323         UC_SRCE_TRACE("           with type  %" PRIu16, context->cache_address.addressType);
00324         UC_SRCE_TRACE("           with addr  %" PRIx64, data);
00325         UC_SRCE_TRACE("           with space %" PRIu32, context->cache_address_length);
00326 #endif
00327         if (status != PAL_SUCCESS) {
00328             UC_SRCE_ERR_MSG("arm_uc_do_pal_dns_lookup failed %" PRIx32, (uint32_t)status);
00329         }
00330         /* Always return BUSY so that the caller can continue
00331          execution. The actual check for success/failure happens
00332          in the callback (see the comment above). */
00333         return ARM_UC_ERROR(SRCE_ERR_BUSY);
00334     }
00335 }
00336 #if (PAL_DNS_API_VERSION >= 2)
00337 static arm_uc_error_t arm_uc_http_cancel_dns_lookup(void)
00338 {
00339     expecting_dns_callback = false;
00340     // Cancel ongoing asynchronous DNS query (only if non-zero handle).
00341     if (arm_uc_dns_query_handle != 0) {
00342         UC_SRCE_TRACE("cancel address-info-async %" PRIu32, arm_uc_dns_query_handle);
00343         pal_cancelAddressInfoAsync(arm_uc_dns_query_handle);
00344         arm_uc_dns_query_handle = 0;
00345     }
00346     return ARM_UC_ERROR(ERR_NONE);
00347 }
00348 #endif
00349 
00350 
00351 /**
00352  * @brief Simple null of some shared HTTP settings for init, close and terminate.
00353  * @details This is a simple refactored nulling routine, it has no failure modes.
00354  * @param a_context_p A pointer to the socket context that must be cleared.
00355  *                    these are only called internally, with context known non-null.
00356  */
00357 static void arm_uc_http_clear_request_fields(void)
00358 {
00359     context->request_uri = NULL;
00360     context->request_buffer = NULL;
00361     context->request_offset = 0;
00362     context->request_type = RQST_TYPE_NONE;
00363 }
00364 static void arm_uc_http_clear_cached_request_fields(void)
00365 {
00366     context->open_request_offset = 0;
00367     context->open_request_type = RQST_TYPE_NONE;
00368     context->open_request_uri = NULL;
00369     context->open_burst_received = 0;
00370 }
00371 static void arm_uc_http_clear_socket_fields(void)
00372 {
00373     context->socket = NULL;
00374     context->socket_state = STATE_DISCONNECTED;
00375     context->expected_socket_event = SOCKET_EVENT_UNDEFINED;
00376 }
00377 
00378 static void arm_uc_http_clear_dns_cache_fields(void)
00379 {
00380     context->cache_address.addressType = 0;
00381     memset(context->cache_address.addressData, 0, PAL_NET_MAX_ADDR_SIZE);
00382     context->cache_address_length = 0;
00383 }
00384 /**
00385  * @brief Initialize the HTTP stream module.
00386  * @details A memory struct is passed as well as a function pointer for event handling.
00387  * @param context Struct holding all socket-associated global variables.
00388  * @param handler Event handler for signaling when each operation is complete.
00389  * @return Error status.
00390  */
00391 arm_uc_error_t arm_uc_http_socket_initialize(
00392     arm_uc_http_socket_context_t *a_context_p,
00393     void (*a_handler_p)(uintptr_t))
00394 {
00395     UC_SRCE_TRACE_ENTRY(">> %s (%" PRIx32 ", %" PRIx32 ") ..", __func__,
00396                         (uint32_t)a_context_p, (uint32_t)a_handler_p);
00397     ARM_UC_INIT_ERROR(status, ERR_NONE);
00398 
00399     if ((a_context_p == NULL) || (a_handler_p == NULL)) {
00400         UC_SRCE_ERR_MSG("error: &context = %" PRIxPTR ", &handler == %" PRIxPTR,
00401                         (uintptr_t)a_context_p, (uintptr_t)a_handler_p);
00402         ARM_UC_SET_ERROR(status, SRCE_ERR_UNINITIALIZED);
00403     }
00404     if (ARM_UC_IS_NOT_ERROR(status)) {
00405         context = a_context_p;
00406 
00407         /* Initialize global variables */
00408         arm_uc_http_clear_request_fields();
00409         arm_uc_http_clear_socket_fields();
00410 
00411         context->callback_handler = a_handler_p;
00412 
00413         arm_uc_http_clear_dns_cache_fields();
00414     }
00415     if (ARM_UC_IS_ERROR(status)) {
00416         UC_SRCE_TRACE("warning: on socket initialize = %" PRIx32, (uint32_t)status.code);
00417         ARM_UCS_Http_SetError(status);
00418     }
00419     return status;
00420 }
00421 
00422 /**
00423  * @brief Close socket, set internal state to disconnected.
00424  */
00425 arm_uc_error_t arm_uc_http_socket_close(void)
00426 {
00427     UC_SRCE_TRACE_ENTRY(">> %s ..", __func__);
00428     ARM_UC_INIT_ERROR(status, ERR_NONE);
00429 
00430     if (context == NULL) {
00431         UC_SRCE_ERR_MSG("error: &context = NULL");
00432         ARM_UC_SET_ERROR(status, SRCE_ERR_UNINITIALIZED);
00433     }
00434     if (ARM_UC_IS_NOT_ERROR(status)) {
00435         /* Close socket if not NULL */
00436         if (context->socket != NULL) {
00437             context->socket_state = STATE_DISCONNECTED;
00438             context->expected_socket_event = SOCKET_EVENT_UNDEFINED;
00439             palStatus_t pal_status = pal_close(&context->socket);
00440             if (pal_status != PAL_SUCCESS) {
00441                 ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
00442             }
00443         }
00444     }
00445     if (ARM_UC_IS_ERROR(status)) {
00446         UC_SRCE_TRACE("warning: on socket close = %" PRIx32, (uint32_t)status.code);
00447         ARM_UCS_Http_SetError(status);
00448     }
00449     return status;
00450 }
00451 
00452 /**
00453  * @brief Closes HTTP socket, resets to uninitialised state, clears memory struct, nulls context.
00454  * @details HTTP sockets must be initialised again before use.
00455  * @return Error code.
00456  */
00457 arm_uc_error_t arm_uc_http_socket_terminate(void)
00458 {
00459     UC_SRCE_TRACE_ENTRY(">> %s ..", __func__);
00460     ARM_UC_INIT_ERROR(status, ERR_NONE);
00461 
00462     if (context == NULL) {
00463         UC_SRCE_ERR_MSG("error: &context = NULL");
00464         ARM_UC_SET_ERROR(status, SRCE_ERR_UNINITIALIZED);
00465     }
00466     if (ARM_UC_IS_NOT_ERROR(status)) {
00467         status = arm_uc_http_socket_close();
00468         arm_uc_http_clear_request_fields();
00469         arm_uc_http_clear_cached_request_fields();
00470         arm_uc_http_clear_socket_fields();
00471         arm_uc_http_clear_dns_cache_fields();
00472         context = NULL;
00473     }
00474     if (ARM_UC_IS_ERROR(status)) {
00475         UC_SRCE_TRACE("warning: on socket terminate = %" PRIx32, (uint32_t)status.code);
00476         ARM_UCS_Http_SetError(status);
00477     }
00478     return status;
00479 }
00480 
00481 /**
00482  * @brief Clean-up after error, but hold on for possible resumes.
00483  * @details Close socket, set internal state to disconnected and generate error event.
00484  * @params error The type of error event that has occurred.
00485  * @return Error status.
00486  */
00487 static arm_ucs_http_event_t last_http_error_event = ERR_NONE;
00488 static arm_uc_error_t arm_uc_http_socket_error(
00489     arm_ucs_http_event_t an_error)
00490 {
00491     UC_SRCE_TRACE(">> %s (%" PRIx32 ") ..", __func__, (uint32_t)an_error);
00492     ARM_UC_INIT_ERROR(status, ERR_NONE);
00493     last_http_error_event = an_error;
00494 
00495     if (context == NULL) {
00496         UC_SRCE_ERR_MSG("error: &context = NULL");
00497         ARM_UC_SET_ERROR(status, SRCE_ERR_UNINITIALIZED);
00498     } else {
00499         arm_uc_http_clear_dns_cache_fields();
00500         if (context->socket_state != STATE_DISCONNECTED) {
00501             status = arm_uc_http_socket_close();
00502         }
00503     }
00504     if (ARM_UC_IS_ERROR(status)) {
00505         UC_SRCE_TRACE("warning: on socket error = %" PRIx32, (uint32_t)status.code);
00506         ARM_UCS_Http_SetError(status);
00507     }
00508     return status;
00509 }
00510 
00511 /**
00512  * @brief Clean-up after final fatal error, no holding on for resumes.
00513  * @details Close socket, set internal state to disconnected and generate error event.
00514  * @params error The type of error event that has occurred.
00515  * @return Error status.
00516  */
00517 arm_uc_error_t arm_uc_http_socket_fatal_error(
00518     arm_ucs_http_event_t an_error)
00519 {
00520     UC_SRCE_ERR_MSG(">> %s (%" PRIx32 ") ..", __func__, (uint32_t)an_error);
00521     ARM_UC_INIT_ERROR(status, ERR_NONE);
00522 
00523     if (context == NULL) {
00524         UC_SRCE_ERR_MSG("error: &context = NULL");
00525         ARM_UC_SET_ERROR(status, SRCE_ERR_UNINITIALIZED);
00526     } else {
00527         arm_uc_http_socket_error(an_error);
00528         /* If callback handler is set, generate error event */
00529         if (context->callback_handler != NULL) {
00530             ARM_UC_PostCallback(NULL, context->callback_handler, an_error);
00531         }
00532     }
00533     if (ARM_UC_IS_ERROR(status)) {
00534         UC_SRCE_TRACE("warning: on fatal socket error = %" PRIx32, (uint32_t)status.code);
00535         ARM_UCS_Http_SetError(status);
00536     }
00537     return status;
00538 }
00539 
00540 // RESOURCE-GET.
00541 // -------------
00542 /**
00543  * @brief Get resource at URI.
00544  * @details Download resource at URI from given offset and store in buffer.
00545  *          Events are generated when download finishes, or on error.
00546  *
00547  * @param uri Pointer to structure with resource location.
00548  * @param buffer Pointer to structure with buffer location, maxSize, and size.
00549  * @param offset Offset in resource to begin download from.
00550  * @param type Indicate what type of request that was initiated.
00551  * @return Error status.
00552  */
00553 arm_uc_error_t arm_uc_http_socket_get(
00554     arm_uc_uri_t *uri,
00555     arm_uc_buffer_t *buffer,
00556     uint32_t offset,
00557     arm_uc_http_rqst_t type)
00558 {
00559     UC_SRCE_TRACE(">> %s (%" PRIx32 ", %" PRIx32 ", %" PRIx32 ", %" PRIx32 ") ..",
00560                         __func__, (uint32_t)uri, (uint32_t)buffer, offset, (uint32_t)type);
00561     ARM_UC_INIT_ERROR(status, ERR_NONE);
00562 
00563     /* Check for NULL pointers before dereferencing them */
00564     if ((context == NULL)
00565             || (uri == NULL)
00566             || (uri->scheme == URI_SCHEME_NONE)
00567             || (uri->host == NULL)
00568             || (uri->path == NULL)
00569             || (buffer == NULL)
00570             || (buffer->ptr == NULL)) {
00571         UC_SRCE_ERR_MSG("error: &context = %" PRIxPTR " or null URI or buffer args", (uintptr_t)context);
00572         ARM_UC_SET_ERROR(status, SRCE_ERR_INVALID_PARAMETER);
00573     }
00574     if (ARM_UC_IS_NOT_ERROR(status)) {
00575         context->request_uri = uri;
00576         context->request_buffer = buffer;
00577         context->request_offset = offset;
00578         context->request_type = type;
00579         context->request_buffer->size = 0;
00580         status = arm_uc_http_install_app_event(SOCKET_EVENT_INITIATE);
00581     }
00582     // Tracing for development debugging.
00583     if (ARM_UC_IS_ERROR(status)) {
00584         UC_SRCE_TRACE("warning: on socket get = %" PRIx32, (uint32_t)status.code);
00585         UC_SRCE_TRACE_VERBOSE("context 0x%" PRIx32" uri 0x%" PRIx32, (uint32_t)context, (uint32_t)uri);
00586         if (uri != NULL) {
00587             UC_SRCE_TRACE_VERBOSE("scheme %" PRIu32" host 0x%" PRIx32, (uint32_t)uri->scheme, (uint32_t)uri->host);
00588             if (uri->host != NULL) {
00589                 UC_SRCE_TRACE_VERBOSE("host %s",uri->host);
00590             }
00591             UC_SRCE_TRACE_VERBOSE("path 0x%" PRIu32, (uint32_t)uri->path);
00592             if (uri->path != NULL) {
00593                 UC_SRCE_TRACE_VERBOSE("path %s", uri->path);
00594             }
00595         }
00596         if (buffer != NULL) {
00597             UC_SRCE_TRACE_VERBOSE("buffer 0x%" PRIx32" buffer.ptr 0x%" PRIx32, (uint32_t)buffer, (uint32_t)buffer->ptr);
00598         }
00599     }
00600     if (ARM_UC_IS_ERROR(status)) {
00601         ARM_UCS_Http_SetError(status);
00602     }
00603     return status;
00604 }
00605 
00606 // CONNECT MANAGEMENT.
00607 // -------------------
00608 
00609 // Connection to the server is optimised by caching an already open connection,
00610 //   and also by caching an already (partially) filled stream. So there are three
00611 //   levels of connection attempt:
00612 //
00613 //   1 - the socket is closed, and needs to be reopened from scratch, and new
00614 //         data needs to be explicitly requested.
00615 //   2 - the socket is open, but there is no data waiting there, presumably because
00616 //         the last read from the socket consumed everything waiting, or because
00617 //         there was an error that did not entail shutting down the socket. This
00618 //         means the socket can be used to request stuff without re-making a new
00619 //         connection to the server, but the new data must be explicitly requested.
00620 //   3 - the socket is open and stream data is available, and more importantly the
00621 //         correct stream data in that it matches what is requested, in which case
00622 //         we do absolutely nothing beyond getting ready to read from the stream.
00623 //         This manifests as a call to arm_uc_http_socket_soft_connect() after
00624 //         arm_uc_open_http_socket_matches_request() has ensured that the necessary
00625 //         conditions are met.
00626 
00627 /**
00628  * @brief Do the full socket connection attempt, from scratch.
00629  * @return Error status.
00630  */
00631 arm_uc_error_t arm_uc_http_socket_connect_new(void)
00632 {
00633     /* Default return value, all code is gated on ERR_NONE */
00634     ARM_UC_INIT_ERROR(status, ERR_NONE);
00635     palStatus_t pal_inner = PAL_SUCCESS;
00636 
00637     if (context == NULL) {
00638         UC_SRCE_ERR_MSG("error: &context = NULL");
00639         ARM_UC_SET_ERROR(status, SRCE_ERR_INVALID_PARAMETER);
00640     } else if (context->cache_address.addressType == 0) {
00641         UC_SRCE_ERR_MSG("warning: cache address type is 0");
00642         ARM_UC_SET_ERROR(status, SRCE_ERR_UNINITIALIZED);
00643     } else if (context->cache_address_length == 0) {
00644         UC_SRCE_ERR_MSG("warning: cache address length is 0");
00645         ARM_UC_SET_ERROR(status, SRCE_ERR_UNINITIALIZED);
00646     }
00647     /* Create new asynchronous PAL socket */
00648     if (ARM_UC_IS_NOT_ERROR(status)) {
00649         if (PAL_SUCCESS == (pal_inner = pal_asynchronousSocket(
00650                                             (palSocketDomain_t) context->cache_address.addressType,
00651                                             PAL_SOCK_STREAM,
00652                                             true,
00653                                             0,
00654                                             arm_uc_http_socket_isr,
00655                                             &context->socket))) {
00656             UC_SRCE_TRACE_VERBOSE("socket: create success");
00657         } else {
00658             UC_SRCE_ERR_MSG("socket creation failed with pal status: 0x%" PRIX32, (uint32_t)pal_inner);
00659             ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
00660         }
00661     }
00662 
00663     /* Convert URI to PAL address *if* cache is not empty */
00664     if (ARM_UC_IS_NOT_ERROR(status)) {
00665         if (context->cache_address_length != 0) {
00666             /* Set PAL port */
00667             if (PAL_SUCCESS == (pal_inner = pal_setSockAddrPort(&context->cache_address,
00668                                                                 context->request_uri->port))) {
00669                 UC_SRCE_TRACE_VERBOSE("socket: set socket address port");
00670             } else {
00671                 UC_SRCE_ERR_MSG("pal_setSockAddrPort returned: 0x%" PRIX32, (uint32_t)pal_inner);
00672                 ARM_UC_SET_ERROR(status, SRCE_ERR_INVALID_PARAMETER);
00673             }
00674         } else {
00675             ARM_UC_SET_ERROR(status, SRCE_ERR_UNINITIALIZED);
00676         }
00677     }
00678 
00679     /* Connect to server */
00680     if (ARM_UC_IS_NOT_ERROR(status)) {
00681         pal_inner = pal_connect(context->socket,
00682                                 &context->cache_address,
00683                                 context->cache_address_length);
00684         UC_SRCE_TRACE("pal_connect returned: 0x%" PRIX32, (uint32_t)pal_inner);
00685         switch (pal_inner) {
00686             case PAL_SUCCESS: /* Synchronous finish */
00687                 /* Move forward to idle state, and fake a connect-done event */
00688                 context->socket_state = STATE_CONNECTED_IDLE;
00689                 arm_uc_http_prepare_skip_to_event(SOCKET_EVENT_CONNECT_DONE);
00690                 break;
00691             case PAL_ERR_SOCKET_IN_PROGRES : /* Asynchronous finish */
00692                 /* The next event should be connect-done, we wait for it */
00693                 // Note that it is set here, not in the event handler, else it risks being lost.
00694                 context->socket_state = STATE_CONNECTING;
00695                 context->expected_socket_event = SOCKET_EVENT_CONNECT_DONE;
00696                 arm_uc_http_prepare_skip_to_event(SOCKET_EVENT_CONNECT_BLOCKED);
00697                 break;
00698             default:
00699                 UC_SRCE_ERR_MSG("warning: socket connection failed");
00700 #if ARM_UC_SOURCE_MANAGER_TRACE_ENABLE
00701                 if (context->cache_address.addressType == PAL_AF_INET) {
00702                     uint32_t address;
00703                     memcpy(&address, &context->cache_address.addressData[0], 4);
00704                     UC_SRCE_TRACE_VERBOSE("IPv4 (cal %" PRIu32 ") ca %" PRIx32,
00705                                           context->cache_address_length, address);
00706 
00707                 } else if (context->cache_address.addressType == PAL_AF_INET6) {
00708                     uint64_t part0, part1;
00709                     memcpy(&part0, &context->cache_address.addressData[0], 8);
00710                     memcpy(&part1, &context->cache_address.addressData[8], 8);
00711                     UC_SRCE_TRACE_VERBOSE("IPv6 (cal %" PRIu32 ") ca %" PRIx64 ":%" PRIx64,
00712                                           context->cache_address_length, part0, part1);
00713 
00714                 }
00715 #endif
00716                 ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
00717                 break;
00718         }
00719     }
00720     UC_SRCE_TRACE_VERBOSE("%s = %s", __func__, ARM_UC_err2Str(status));
00721     if (ARM_UC_IS_ERROR(status)) {
00722         UC_SRCE_TRACE("warning: on socket connect new = %" PRIx32, (uint32_t)status.code);
00723         ARM_UCS_Http_SetError(status);
00724         arm_uc_http_socket_close();
00725     }
00726     return status;
00727 }
00728 
00729 /**
00730  * @brief Decide if a full connection attempt is needed, if not just skip ahead.
00731  * @details Note that we don't even get here if the stream is already active,
00732  *          because in that case the socket connection is reused.
00733  * @return Error status.
00734  */
00735 arm_uc_error_t arm_uc_http_socket_connect(void)
00736 {
00737     UC_SRCE_TRACE_ENTRY(">> %s ..", __func__);
00738     ARM_UC_INIT_ERROR(status, ERR_NONE);
00739 
00740     if (context == NULL) {
00741         UC_SRCE_ERR_MSG("error: &context = NULL");
00742         ARM_UC_SET_ERROR(status, SRCE_ERR_INVALID_PARAMETER);
00743     } else if (context->request_uri == NULL) {
00744         UC_SRCE_ERR_MSG("error: URI = NULL");
00745         ARM_UC_SET_ERROR(status, SRCE_ERR_INVALID_PARAMETER);
00746     }
00747     if (ARM_UC_IS_NOT_ERROR(status)) {
00748         context->open_request_uri = context->request_uri;
00749         context->open_request_type = context->request_type;
00750         context->open_request_offset = context->request_offset;
00751         UC_SRCE_TRACE("%s context->socket_state = %" PRIx16, __func__, (uint16_t)context->socket_state);
00752         switch (context->socket_state) {
00753             case STATE_DISCONNECTED:
00754                 // Make a new socket connection.
00755                 status = arm_uc_http_socket_connect_new();
00756                 break;
00757             case STATE_CONNECTED_IDLE:
00758                 // Socket is already connected, but not busy, so progress state machine.
00759                 status = arm_uc_http_prepare_skip_to_event(SOCKET_EVENT_CONNECT_DONE);
00760                 // Otherwise result is already set to the error condition.
00761                 break;
00762             default:
00763                 // Socket is already busy, either connecting or communicating.
00764                 ARM_UC_SET_ERROR(status, SRCE_ERR_BUSY);
00765                 break;
00766         }
00767     }
00768     if (ARM_UC_IS_ERROR(status)) {
00769         UC_SRCE_TRACE("warning: on socket connect = %" PRIx32, (uint32_t)status.code);
00770         ARM_UCS_Http_SetError(status);
00771     }
00772     return status;
00773 }
00774 
00775 /**
00776  * @brief Connect softly with an already-open socket.
00777  * @details Avoids making a new connection to the server, just does setup.
00778  * @return Error status.
00779  */
00780 arm_uc_error_t arm_uc_http_socket_soft_connect(void)
00781 {
00782     UC_SRCE_TRACE_ENTRY(">> %s", __func__);
00783     ARM_UC_INIT_ERROR(status, ERR_NONE);
00784 
00785     if (context == NULL) {
00786         UC_SRCE_ERR_MSG("error: &context = NULL");
00787         ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
00788     }
00789     if (ARM_UC_IS_ERROR(status)) {
00790         UC_SRCE_TRACE("warning: on socket soft connect = %" PRIx32, (uint32_t)ARM_UC_GET_ERROR(status));
00791         ARM_UCS_Http_SetError(status);
00792     }
00793     return status;
00794 }
00795 
00796 // SEND HANDLING.
00797 // --------------
00798 
00799 /**
00800  * @brief Construct and send the request packet that fetches the next chunk from the server.
00801  * @details If the state machine decides that it needs to get more data from the server,
00802  *          here is where it happens. (The FSM will not come here if the socket is already
00803  *          open and has data waiting, for example.) This routine constructs an HTTP request
00804  *          (either HEAD or GET), builds up the body with suitable parts, then sends the
00805  *          request to the server.
00806  *          HEAD is like GET with no body, and is sufficient for TAG and DATE information.
00807  *          The FILE and FRAG requests differ in that a fragment request has a range associated,
00808  *          and it requests only data in that range. A FILE request (which we don't currently use)
00809  *          asks for a whole file, from beginning to end.
00810  *          The streaming implementation uses the FRAG interface, but requests a fragment that
00811  *          is the size of all remaining (unfetched) data in the file, and then pulls that
00812  *          data out in pieces from the socket, thereafter short-circuiting calls to this
00813  *          routine wherever possible. This is because the cost of copying the next buffered
00814  *          data from the socket is tiny compared to the cost of constructing a new fragment
00815  *          GET, sending the GET (data tx time and link delays), awaiting a response, receiving
00816  *          (link delays and rx time) and then decoding the response.
00817  * @return Error status.
00818  */
00819 arm_uc_error_t arm_uc_http_socket_send_request(void)
00820 {
00821     UC_SRCE_TRACE_ENTRY(">> %s ..", __func__);
00822     ARM_UC_INIT_ERROR(status, ERR_NONE);
00823     if (context == NULL) {
00824         UC_SRCE_ERR_MSG("error: &context = NULL");
00825         ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
00826     } else if (context->request_buffer == NULL) {
00827         UC_SRCE_ERR_MSG("error: context->request_buffer = NULL");
00828         ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
00829     }
00830 
00831     /* Get local references */
00832     arm_uc_buffer_t *request_buffer = NULL;
00833     arm_uc_uri_t *request_uri = NULL;
00834     arm_uc_http_rqst_t request_type = RQST_TYPE_NONE;
00835 
00836     char *req_type_str = NULL;
00837 
00838     /* Make appropriate HTTP request for required type */
00839     if (ARM_UC_IS_NOT_ERROR(status)) {
00840         request_buffer = context->request_buffer;
00841         request_uri = context->request_uri;
00842         request_type = context->request_type;
00843 
00844         switch (request_type) {
00845             case RQST_TYPE_HASH_ETAG:
00846             case RQST_TYPE_HASH_DATE:
00847                 /* Construct ETag and Date request header */
00848                 req_type_str = "HEAD";
00849                 break;
00850             case RQST_TYPE_GET_FILE:
00851             case RQST_TYPE_GET_FRAG:
00852                 /* Construct download header */
00853                 req_type_str = "GET";
00854                 break;
00855             default:
00856                 UC_SRCE_ERR_MSG("warning: on send request = %" PRIx32" (invalid request type)", (uint32_t)status.code);
00857                 ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
00858                 break;
00859         }
00860     }
00861 
00862     if (ARM_UC_IS_NOT_ERROR(status)) {
00863         /* Template for generating HTTP requests */
00864         static const char HTTP_HEADER_TEMPLATE[] =
00865             "%s %s HTTP/1.1\r\n" // Status line
00866             "Host: %s\r\n";// Mandated for HTTP 1.1
00867 
00868         /* Construct download header */
00869         request_buffer->size = snprintf((char *) request_buffer->ptr,
00870                                         request_buffer->size_max,
00871                                         HTTP_HEADER_TEMPLATE,
00872                                         req_type_str,
00873                                         request_uri->path,
00874                                         request_uri->host);
00875         /* If fragment then construct the Range field that makes this a partial content request */
00876         if (request_type == RQST_TYPE_GET_FRAG) {
00877             context->open_burst_requested = request_buffer->size_max * frags_per_burst;
00878             if(context->open_burst_requested) {
00879                 uint32_t http_segment_end = 0;
00880                 uint32_t burst_default = (context->open_burst_requested - 1);
00881                 if ((context->request_offset > UINT32_MAX - burst_default)) {
00882                     /* preventing overflow */;
00883                     http_segment_end = UINT32_MAX;
00884                 } else {
00885                     http_segment_end=context->request_offset + burst_default;
00886                 }
00887 
00888                 request_buffer->size += snprintf((char *) request_buffer->ptr + request_buffer->size,
00889                                                  request_buffer->size_max - request_buffer->size,
00890                                                  "Range: bytes=%" PRIu32 "-%" PRIu32 "\r\n",
00891                                                  context->request_offset,
00892                                                  http_segment_end);
00893             } else {
00894                 // just request remaining file from start offset
00895                 request_buffer->size += snprintf((char *) request_buffer->ptr + request_buffer->size,
00896                                      request_buffer->size_max - request_buffer->size,
00897                                      "Range: bytes=%" PRIu32 "-\r\n",
00898                                      context->request_offset
00899                                      );
00900             }
00901         }
00902         /* Terminate request with a carriage return and newline */
00903         request_buffer->size += snprintf((char *) request_buffer->ptr + request_buffer->size,
00904                                          request_buffer->size_max - request_buffer->size,
00905                                          "\r\n");
00906 
00907         /* Terminate string */
00908         request_buffer->ptr[request_buffer->size] = '\0';
00909         UC_SRCE_TRACE("%s \r\n %s  \r\n  request_buffer->size %"PRIx32"\r\n request_buffer->size_max %"PRIx32,
00910                       __func__, request_buffer->ptr, request_buffer->size,request_buffer->size_max);
00911 
00912     }
00913 
00914     if (ARM_UC_IS_NOT_ERROR(status)) {
00915         /* Send HTTP request */
00916         size_t bytes_sent = 0;
00917         context->expected_socket_event = SOCKET_EVENT_SEND_DONE;
00918         palStatus_t pal_result = pal_send(context->socket,
00919                                           request_buffer->ptr,
00920                                           request_buffer->size,
00921                                           &bytes_sent);
00922         switch (pal_result) {
00923             case PAL_SUCCESS: /* Synchronous finish */
00924                 UC_SRCE_TRACE("%s send success", __func__);
00925                 /* Reset buffer and prepare to receive header */
00926                 request_buffer->size = 0;
00927                 context->socket_state = STATE_PROCESS_HEADER;
00928                 context->expected_socket_event = SOCKET_EVENT_SEND_DONE;
00929                 break;
00930             case PAL_ERR_SOCKET_WOULD_BLOCK : /* Asynchronous finish */
00931                 UC_SRCE_TRACE("%s send would block, will retry",__func__);
00932                 /* Keep current state and force callback to retry sending */
00933                 // Note that it is set here, not in the event handler, else it risks being lost.
00934                 arm_uc_http_prepare_skip_to_event(SOCKET_EVENT_SEND_BLOCKED);
00935                 break;
00936             default:
00937                 context->expected_socket_event = SOCKET_EVENT_UNDEFINED;
00938                 arm_uc_http_prepare_skip_to_event(SOCKET_EVENT_SEND_BLOCKED);
00939                 break;
00940         }
00941     }
00942 
00943     if (ARM_UC_IS_ERROR(status)) {
00944         UC_SRCE_TRACE("warning: on socket send request = %" PRIx32, (uint32_t)status.code);
00945         ARM_UCS_Http_SetError(status);
00946     }
00947     return status;
00948 }
00949 
00950 // RECEIVE HANDLING.
00951 // -----------------
00952 
00953 /**
00954  * @brief Receive data from HTTP socket.
00955  * @details Data is stored in global buffer. The call will automatically retry
00956  *          if the socket is busy.
00957  * @return Error status.
00958  */
00959 // Just receive more data, all processing is vectored from the event handler.
00960 arm_uc_error_t arm_uc_http_socket_receive(void)
00961 {
00962     UC_SRCE_TRACE_ENTRY(">> %s [expected-event %" PRIu32 "] ..", __func__, (uint32_t)context->expected_socket_event);
00963     ARM_UC_INIT_ERROR(status, ERR_NONE);
00964     palStatus_t pal_result = PAL_SUCCESS;
00965     if (context == NULL) {
00966         UC_SRCE_ERR_MSG("error: &context = NULL");
00967         ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
00968     } else if (context->request_buffer == NULL) {
00969         UC_SRCE_ERR_MSG("error: context->request_buffer = NULL");
00970         ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
00971     } else if (context->socket_state == STATE_DISCONNECTED) {
00972         UC_SRCE_ERR_MSG("error: socket is disconnected");
00973         ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
00974     }
00975 
00976     arm_uc_buffer_t *request_buffer = NULL;
00977     size_t available_space = 0;
00978 
00979     if (ARM_UC_IS_NOT_ERROR(status)) {
00980         request_buffer = context->request_buffer;
00981         available_space = request_buffer->size_max - request_buffer->size;
00982         UC_SRCE_TRACE_VERBOSE("  space %" PRIu32, (uint32_t)available_space);
00983 
00984         if (available_space <= 0) {
00985             ARM_UC_SET_ERROR(status, SRCE_ERR_ABORT);
00986             UC_SRCE_ERR_MSG("warning: on socket receive = %" PRIx32 " (no buffer space)", (uint32_t)status.code);
00987             arm_uc_http_socket_error(UCS_HTTP_EVENT_ERROR_BUFFER_SIZE);
00988         }
00989     }
00990 
00991     if (ARM_UC_IS_NOT_ERROR(status)) {
00992         size_t received_bytes = 0;
00993         /* Append data from socket receive buffer to request buffer. */
00994         pal_result = pal_recv(context->socket,
00995                               &(request_buffer->ptr[request_buffer->size]),
00996                               available_space,
00997                               &received_bytes);
00998         UC_SRCE_TRACE("%s  \r\n request_buffer->size %"PRIx32"\r\n request_buffer->size_max %"PRIx32,
00999                       __func__, request_buffer->size,request_buffer->size_max);
01000         switch (pal_result) {
01001             case PAL_SUCCESS:
01002                 if (received_bytes <= 0) {
01003                     ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
01004                     UC_SRCE_ERR_MSG("warning: socket failed - received zero or less bytes");
01005                     arm_uc_http_socket_error(UCS_HTTP_EVENT_ERROR);
01006                 } else {
01007                     /* Note: the proper formatter %zu is not supported on mbed's libc,
01008                      * hence the casts to difference type.
01009                      */
01010                     UC_SRCE_TRACE("recv success: %lu bytes received", (unsigned long)received_bytes);
01011 
01012                     if (received_bytes > available_space) {
01013                         ARM_UC_SET_ERROR(status, SRCE_ERR_ABORT);
01014                         UC_SRCE_ERR_MSG("warning: socket receive - data exceeds space");
01015                         arm_uc_http_socket_error(UCS_HTTP_EVENT_ERROR_BUFFER_SIZE);
01016                         break;
01017                     }
01018                     /* Update buffer size with received bytes */
01019                     request_buffer->size += received_bytes;
01020 
01021                     // if this is a header then these actions will be temporarily wrong.
01022                     // the offset and received values will have to be adjusted later,
01023                     //   as they are intended to refer to the payload, not the total data received.
01024                     context->open_request_offset += received_bytes;
01025                     context->open_burst_received += received_bytes;
01026 
01027                     UC_SRCE_TRACE("open_request_offset %" PRIu32, context->open_request_offset);
01028                 }
01029                 break;
01030             case PAL_ERR_SOCKET_WOULD_BLOCK :
01031                 UC_SRCE_TRACE("recv: pending: %" PRIu32, (request_buffer->size_max - request_buffer->size));
01032                 // This error code is not actually an error, so don't report it if error-tracing is enabled.
01033                 ARM_UC_SET_ERROR_NEVER_TRACE(status, SRCE_ERR_BUSY);
01034                 break;
01035             default:
01036                 ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
01037                 UC_SRCE_ERR_MSG("warning: socket receive failed");
01038                 arm_uc_http_socket_error(UCS_HTTP_EVENT_ERROR);
01039                 break;
01040         }
01041     }
01042 
01043     /* There's an error, but it isn't just that we are going asynch */
01044     if (ARM_UC_IS_ERROR(status)
01045             && (ARM_UC_GET_ERROR(status) != SRCE_ERR_BUSY)) {
01046         UC_SRCE_TRACE("warning: on socket receive = %" PRIx32, (uint32_t)status.code);
01047         ARM_UCS_Http_SetError(status);
01048     }
01049     return status;
01050 }
01051 
01052 // HEADER HANDLING.
01053 // ----------------
01054 
01055 /**
01056  * @brief Check to see if a header we are waiting for has arrived.
01057  * @param a_has_received_p (out) Indicator of header arrived or not.
01058  * @return Error status.
01059  */
01060 arm_uc_error_t arm_uc_http_socket_has_received_header(
01061     bool *a_has_received_p)
01062 {
01063     UC_SRCE_TRACE_ENTRY(">> %s ..", __func__);
01064     ARM_UC_INIT_ERROR(status, ERR_NONE);
01065     if (context == NULL) {
01066         UC_SRCE_ERR_MSG("error: &context = NULL");
01067         ARM_UC_SET_ERROR(status, SRCE_ERR_UNINITIALIZED);
01068     } else if (a_has_received_p == NULL) {
01069         UC_SRCE_ERR_MSG("error: flag * a_has_received_p = %" PRIxPTR, (uintptr_t)a_has_received_p);
01070         ARM_UC_SET_ERROR(status, SRCE_ERR_INVALID_PARAMETER);
01071     } else {
01072         context->header_end_index = arm_uc_strnstrn(
01073                                         context->request_buffer->ptr,
01074                                         context->request_buffer->size,
01075                                         (const uint8_t *) "\r\n\r\n",
01076                                         4);
01077         *a_has_received_p = (context->header_end_index <= context->request_buffer->size);
01078         // Raise an error if we have filled the buffer,
01079         //   but there isn't enough space to hold a full header.
01080         if (!*a_has_received_p
01081                 && (context->request_buffer->size == context->request_buffer->size_max)) {
01082             UC_SRCE_ERR_MSG("warning: socket receive - not enough space for a header");
01083 #if ARM_UC_SOURCE_MANAGER_TRACE_ENABLE
01084             context->request_buffer->ptr[context->request_buffer->size - 1] = 0;
01085             UC_SRCE_TRACE("received\r\n%s", context->request_buffer->ptr);
01086 #endif
01087             arm_uc_http_socket_error(UCS_HTTP_EVENT_ERROR_BUFFER_SIZE);
01088             ARM_UC_SET_ERROR(status, SRCE_ERR_ABORT);
01089         }
01090     }
01091 
01092     if (ARM_UC_IS_ERROR(status)) {
01093         UC_SRCE_TRACE("warning: on socket received header = %" PRIx32, (uint32_t)status.code);
01094         ARM_UCS_Http_SetError(status);
01095     }
01096     return status;
01097 }
01098 
01099 /**
01100  * @brief Received HTTP redirect codes, handle these.
01101  * @details If we received a redirect, we copy the redirect URI and rerequest.
01102  *        301: Moved Permanently
01103  *        302: Found [Elsewhere]
01104  *        303: See Other
01105  *        307: Temporary Redirect
01106  * @param an_http_status_code The actual code received.
01107  * @return Error status.
01108  */
01109 arm_uc_error_t arm_uc_http_socket_process_header_redirect_codes(
01110     uint32_t an_http_status_code)
01111 {
01112     UC_SRCE_TRACE_ENTRY(">> %s ..", __func__);
01113     ARM_UC_INIT_ERROR(status, ERR_NONE);
01114 
01115     if (context == NULL) {
01116         UC_SRCE_ERR_MSG("error: &context = NULL");
01117         ARM_UC_SET_ERROR(status, SRCE_ERR_UNINITIALIZED);
01118     } else if (context->request_buffer == NULL) {
01119         UC_SRCE_ERR_MSG("error: context->request_buffer = NULL");
01120         ARM_UC_SET_ERROR(status, SRCE_ERR_UNINITIALIZED);
01121     }
01122     arm_uc_buffer_t *request_buffer = NULL;
01123     arm_uc_uri_t *request_uri = NULL;
01124 
01125     if (ARM_UC_IS_NOT_ERROR(status)) {
01126         request_buffer = context->request_buffer;
01127         request_uri = context->request_uri;
01128 
01129         /* Move location to front of buffer */
01130         const char tag[] = "Location";
01131         bool found = arm_uc_http_socket_trim_value(request_buffer, tag, sizeof(tag) - 1);
01132         if (!found) {
01133             // The file isn't there, *and* there's no redirect, so abort the operation.
01134             UC_SRCE_ERR_MSG("warning: unable to find redirect location");
01135             ARM_UC_SET_ERROR(status, SRCE_ERR_ABORT);
01136         }
01137     }
01138     if (ARM_UC_IS_NOT_ERROR(status)) {
01139 
01140         /* NULL terminate string */
01141         request_buffer->ptr[request_buffer->size] = '\0';
01142 
01143         /* Parse location and store in URI */
01144         status = arm_uc_str2uri(request_buffer->ptr,
01145                                 request_buffer->size,
01146                                 request_uri);
01147         if (ARM_UC_IS_ERROR(status)
01148                 || (request_uri->scheme != URI_SCHEME_HTTP)) {
01149             UC_SRCE_ERR_MSG("unable to parse URI string");
01150             ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
01151         }
01152     }
01153     if (ARM_UC_IS_NOT_ERROR(status)) {
01154         UC_SRCE_TRACE("HTTP redirecting to http://%s:%" PRIu16 "/%s",
01155                       request_uri->host,
01156                       request_uri->port,
01157                       request_uri->path);
01158 
01159         // Drop anything remaining in the buffer, it has no value now,
01160         //   and just gets in the way in future operations.
01161         request_buffer->size = 0;
01162 
01163         // For now, assume that socket and DNS must be refreshed,
01164         //   since although it might be avoidable, it is complicated by the
01165         //   overwrite during parsing, and isn't a huge win anyway.
01166         bool host_is_new = true, port_is_new = true;
01167         // based on the changed values, refresh everything needing it.
01168         if (port_is_new) {
01169             // Need to close (and later reopen) the socket if the port differs.
01170             arm_uc_http_socket_close();
01171             arm_uc_http_socket_event_t event = SOCKET_EVENT_CONNECT_START;
01172             if (host_is_new) {
01173                 // Need to refresh the DNS cache if the host differs.
01174                 // Flush the socket DNS cache, and restart the process with the new URI.
01175                 arm_uc_http_clear_dns_cache_fields();
01176                 event = SOCKET_EVENT_LOOKUP_START;
01177             }
01178             arm_uc_http_install_app_event(event);
01179             ARM_UC_SET_ERROR(status, SRCE_ERR_BUSY);
01180         }
01181     }
01182     if (ARM_UC_IS_ERROR(status)) {
01183         UC_SRCE_TRACE("warning: on socket received redirect header = %" PRIx32, (uint32_t)status.code);
01184         ARM_UCS_Http_SetError(status);
01185     }
01186     return status;
01187 }
01188 
01189 /**
01190  * @brief Post an invocation for the handler of a receive event.
01191  * @param a_http_event The type of event we want handled.
01192  * @return Error status.
01193  */
01194 arm_uc_error_t arm_uc_http_socket_process_header_post_callback(
01195     arm_ucs_http_event_t an_http_event)
01196 {
01197     ARM_UC_INIT_ERROR(status, ERR_NONE);
01198 
01199     if (context == NULL) {
01200         UC_SRCE_ERR_MSG("error: &context = NULL");
01201         ARM_UC_SET_ERROR(status, SRCE_ERR_UNINITIALIZED);
01202     }
01203     if (ARM_UC_IS_NOT_ERROR(status)) {
01204         /* Was already successfully read - post callback */
01205         if (context->callback_handler) {
01206             ARM_UC_PostCallback(NULL, context->callback_handler, an_http_event);
01207         }
01208         /* Request complete - close socket */
01209         status = arm_uc_http_socket_close();
01210     }
01211     if (ARM_UC_IS_ERROR(status)) {
01212         UC_SRCE_TRACE("warning: on socket received header = %" PRIx32, (uint32_t)status.code);
01213         ARM_UCS_Http_SetError(status);
01214     }
01215     return status;
01216 }
01217 
01218 /**
01219  * @brief Got a header, check out the return code.
01220  * @param an_http_status_code The actual code received.
01221  * @return Error status.
01222  */
01223 arm_uc_error_t arm_uc_http_socket_process_header_return_codes(
01224     uint32_t an_http_status_code)
01225 {
01226 // TODO check out this below, because 206 = Partial Content, which is no error.
01227 
01228     /* NOTE: HTTP 1.1 Code 206 with Header "Connection:close" is not
01229      handled here, instead the execution falls through to error-
01230      handling in http_socket (ARM_UCS_HTTPEvent with UCS_HTTP_EVENT_ERROR)
01231      where the retry-mechanism will reestablish firmware download if
01232      the server closed the connection.
01233      */
01234     UC_SRCE_TRACE_ENTRY(">> %s .. status: %u", __func__, an_http_status_code);
01235     ARM_UC_INIT_ERROR(status, ERR_NONE);
01236 
01237     if (context == NULL) {
01238         UC_SRCE_ERR_MSG("error: &context = NULL");
01239         ARM_UC_SET_ERROR(status, SRCE_ERR_UNINITIALIZED);
01240     } else if (context->request_buffer == NULL) {
01241         UC_SRCE_ERR_MSG("error: context->request_buffer = NULL");
01242         ARM_UC_SET_ERROR(status, SRCE_ERR_UNINITIALIZED);
01243     }
01244     if (ARM_UC_IS_NOT_ERROR(status)) {
01245         arm_uc_buffer_t *request_buffer = context->request_buffer;
01246         UC_SRCE_TRACE("%s an_http_status_code %" PRIx32" context->request_type %" PRIx32,
01247                       __func__,an_http_status_code,(uint32_t)context->request_type);
01248         switch (context->request_type) {
01249             case RQST_TYPE_HASH_ETAG: {
01250                 /* Look for ETag and move to front of buffer */
01251                 const char tag[] = "ETag";
01252                 bool found = arm_uc_http_socket_trim_value(request_buffer, tag, sizeof(tag) - 1);
01253                 if (!found) {
01254                     UC_SRCE_ERR_MSG("unable to find ETag");
01255                     ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
01256                 } else {
01257                     /* ETag successfully read - post callback */
01258                     status = arm_uc_http_socket_process_header_post_callback(UCS_HTTP_EVENT_HASH);
01259                 }
01260             }
01261             break;
01262             case RQST_TYPE_HASH_DATE: {
01263                 /* Look for date and move to front of buffer */
01264                 const char tag[] = "Last-Modified";
01265                 bool found = arm_uc_http_socket_trim_value(request_buffer, tag, sizeof(tag) - 1);
01266                 if (!found) {
01267                     UC_SRCE_ERR_MSG("unable to find last modified date");
01268                     ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
01269                 } else {
01270                     /* Date successfully read - post callback */
01271                     status = arm_uc_http_socket_process_header_post_callback(UCS_HTTP_EVENT_DATE);
01272                 }
01273             }
01274             break;
01275             case RQST_TYPE_GET_FILE:
01276             case RQST_TYPE_GET_FRAG: {
01277                 /* Save current buffer size so we can recover body after the content length has been read. */
01278                 uint32_t current_size = request_buffer->size;
01279                 uint32_t content_length = 0;
01280 
01281                 /* Find content length and move value to front of buffer */
01282                 const char tag[] = "Content-Length";
01283                 bool found = arm_uc_http_socket_trim_value(request_buffer, tag, sizeof(tag) - 1);
01284                 if (!found) {
01285                     UC_SRCE_ERR_MSG("unable find content length");
01286                     ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
01287                 }
01288                 if (ARM_UC_IS_NOT_ERROR(status)) {
01289                     /* NULL-terminate string */
01290                     // Check this doesn't overrun, trim behaviour isn't guaranteed.
01291                     if (request_buffer->size < request_buffer->size_max) {
01292                         request_buffer->ptr[request_buffer->size] = '\0';
01293                     } else {
01294                         ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
01295                     }
01296                 }
01297                 if (ARM_UC_IS_NOT_ERROR(status)) {
01298                     /* Parse full length of content */
01299                     int parsed = sscanf((char *) request_buffer->ptr, "%10" SCNu32, &content_length);
01300                     /* Only continue if exactly one argument was parsed */
01301                     if (parsed != 1) {
01302                         UC_SRCE_ERR_MSG("unable to parse content length");
01303                         ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
01304                     }
01305                 }
01306                 if (ARM_UC_IS_NOT_ERROR(status)) {
01307                     UC_SRCE_TRACE_VERBOSE("content-length: %" PRIu32, content_length);
01308 
01309                     /* Replace HTTP header with body */
01310                     uint32_t header_size = context->header_end_index + 4;
01311                     uint32_t body_size = current_size - header_size;
01312                     memmove(request_buffer->ptr,
01313                             &(request_buffer->ptr[context->header_end_index + 4]),
01314                             body_size);
01315 
01316                     /* Set size of partial body, also reset burst info */
01317                     request_buffer->size = body_size;
01318                     context->open_request_offset = context->request_offset + request_buffer->size;
01319                     context->open_burst_expected = content_length;
01320                     context->open_burst_received = request_buffer->size;
01321 
01322                     if (content_length < (request_buffer->size_max * frags_per_burst)) {
01323                         UC_SRCE_TRACE_VERBOSE("last burst in flight! %" PRIu32 " of burst %" PRIu32,
01324                                               content_length, (request_buffer->size_max * frags_per_burst));
01325                     }
01326                     if (request_buffer->size < request_buffer->size_max) {
01327                         /* Expecting more data - continue receiving */
01328                         UC_SRCE_TRACE_VERBOSE("expecting more fragment data after header (got %" PRIu32 " of %" PRIu32 " max)",
01329                                               request_buffer->size,
01330                                               request_buffer->size_max);
01331                     }
01332                     UC_SRCE_TRACE_VERBOSE("burst data received after header %" PRIu32,
01333                                           context->open_burst_received);
01334                     /* Continue processing body */
01335                     context->socket_state = STATE_PROCESS_BODY;
01336                     // Finishing with status.code == ERR_NONE
01337                 }
01338 
01339             }
01340             break;
01341             default:
01342                 UC_SRCE_ERR_MSG("%s warning: unknown request type", __func__);
01343                 ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
01344                 break;
01345         }
01346     }
01347 
01348     if (ARM_UC_IS_ERROR(status)) {
01349         UC_SRCE_TRACE("warning: on socket process header return codes = %" PRIx32, (uint32_t)status.code);
01350         ARM_UCS_Http_SetError(status);
01351     }
01352     return status;
01353 }
01354 
01355 /**
01356  * @brief Received a header, process it.
01357  * @details This only runs if a header has actually arrived.
01358  *          First checks that the HTTP encoding is correct.
01359  *          Then checks that there is a status code and handles as appropriate.
01360  * @param an_http_status_code The actual code received.
01361  * @return Error status.
01362  */
01363 arm_uc_error_t arm_uc_http_socket_process_header(void)
01364 {
01365     UC_SRCE_TRACE_ENTRY(">> %s ..", __func__);
01366     ARM_UC_INIT_ERROR(status, ERR_NONE);
01367 
01368     if (context == NULL) {
01369         UC_SRCE_ERR_MSG("error: &context = NULL");
01370         ARM_UC_SET_ERROR(status, SRCE_ERR_UNINITIALIZED);
01371     } else if (context->request_buffer == NULL) {
01372         UC_SRCE_ERR_MSG("error: context->request_buffer = NULL");
01373         ARM_UC_SET_ERROR(status, SRCE_ERR_UNINITIALIZED);
01374     }
01375     arm_uc_buffer_t *request_buffer = NULL;
01376     uint32_t header_start = 0;
01377 
01378     if (ARM_UC_IS_NOT_ERROR(status)) {
01379         request_buffer = context->request_buffer;
01380 
01381         // Only arrives here if it actually has a header to process.
01382         /* Get local references */
01383         UC_SRCE_TRACE_VERBOSE("HTTP header terminator found");
01384         const char header_tag[] = "HTTP/1.1 ";
01385         header_start = arm_uc_strnstrn(request_buffer->ptr,
01386                                        request_buffer->size,
01387                                        (const uint8_t *) header_tag,
01388                                        sizeof(header_tag) - 1);
01389         /* Found beginning of header */
01390         /* Do buffer size check */
01391         if (header_start < request_buffer->size) {
01392             UC_SRCE_TRACE("HTTP/1.1 header found");
01393             UC_SRCE_TRACE_VERBOSE("HTTP header: \r\n%s", &(request_buffer->ptr[header_start]));
01394             /* Status code is after the header tag */
01395             header_start = header_start + sizeof(header_tag) - 1;
01396         } else {
01397             UC_SRCE_ERR_MSG("Error: HTTP/1.1 header not found");
01398             ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
01399         }
01400     }
01401     uint32_t http_status_code = 0;
01402     if (ARM_UC_IS_NOT_ERROR(status)) {
01403         /* Parse status code */
01404         bool header_parsed = false;
01405         http_status_code = arm_uc_str2uint32(
01406                                &(request_buffer->ptr[header_start]),
01407                                request_buffer->size - header_start,
01408                                &header_parsed);
01409         UC_SRCE_TRACE("%s http_status_code %"PRIx32, __func__, (uint32_t)http_status_code);
01410         if (!header_parsed) {
01411             UC_SRCE_ERR_MSG("warning: unable to read status code");
01412             ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
01413         }
01414     }
01415     if (ARM_UC_IS_NOT_ERROR(status)) {
01416         if (((http_status_code >= 301) && (http_status_code <= 303))
01417                 || (http_status_code == 307)) {
01418             status = arm_uc_http_socket_process_header_redirect_codes(http_status_code);
01419             if (ARM_UC_IS_ERROR(status)) {
01420                 UC_SRCE_ERR_MSG("warning: processing HTTP status code %" PRIx32, http_status_code);
01421             }
01422         }
01423         /* All codes between 200 to 226 */
01424         else if ((http_status_code >= 200) && (http_status_code <= 226)) {
01425             status = arm_uc_http_socket_process_header_return_codes(http_status_code);
01426             if (ARM_UC_IS_ERROR(status)) {
01427                 UC_SRCE_ERR_MSG("warning: processing HTTP status code %" PRIx32, http_status_code);
01428             }
01429         } else {
01430             /* All remaining codes outside 200-226, are treated as errors */
01431             UC_SRCE_ERR_MSG("warning: server returned HTTP status code %" PRIu32, http_status_code);
01432             ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
01433         }
01434     }
01435     if (ARM_UC_IS_ERROR(status)) {
01436         UC_SRCE_ERR_MSG("warning: on socket process header = %" PRIx32, (uint32_t)status.code);
01437         arm_uc_http_socket_error(UCS_HTTP_EVENT_ERROR);
01438         ARM_UCS_Http_SetError(status);
01439     }
01440     return status;
01441 }
01442 
01443 // BODY HANDLING.
01444 // --------------
01445 
01446 // TODO Check it still works if the code is exactly a page multiple in size, incl. empty.
01447 /**
01448  * @brief Check to see if a fragment we are waiting for has fully arrived.
01449  * @param a_has_received_p (out) indicator of fragment arrived or not.
01450  * @return Error status.
01451  */
01452 arm_uc_error_t arm_uc_http_socket_has_received_frag(
01453     bool *a_has_received_p)
01454 {
01455     UC_SRCE_TRACE_ENTRY(">> %s ..", __func__);
01456     ARM_UC_INIT_ERROR(status, ERR_NONE);
01457 
01458     if (context == NULL) {
01459         UC_SRCE_ERR_MSG("error: &context = NULL");
01460         ARM_UC_SET_ERROR(status, SRCE_ERR_UNINITIALIZED);
01461     } else if (context->request_buffer == NULL) {
01462         UC_SRCE_ERR_MSG("error: context->request_buffer = NULL");
01463         ARM_UC_SET_ERROR(status, SRCE_ERR_UNINITIALIZED);
01464     } else if (a_has_received_p == NULL) {
01465         UC_SRCE_ERR_MSG("error: flag * a_has_received_p = %" PRIxPTR, (uintptr_t)a_has_received_p);
01466         ARM_UC_SET_ERROR(status, SRCE_ERR_INVALID_PARAMETER);
01467     }
01468     if (ARM_UC_IS_NOT_ERROR(status)) {
01469         // Check if this is enough to make up a full fragment.
01470         *a_has_received_p = context->request_buffer->size >= context->request_buffer->size_max;
01471         UC_SRCE_TRACE_VERBOSE("  has received full frag? %s %" PRIu32 " of %" PRIu32,
01472                               (*a_has_received_p ? "yes" : "no"),
01473                               context->request_buffer->size,
01474                               context->request_buffer->size_max);
01475         // If not a full fragment, then maybe at the end of the transfer with a short burst.
01476         if (!*a_has_received_p) {
01477             *a_has_received_p = context->open_burst_received == context->open_burst_expected;
01478             if (*a_has_received_p) {
01479                 UC_SRCE_TRACE_VERBOSE("  received short burst complete");
01480             }
01481         }
01482     }
01483     if (ARM_UC_IS_ERROR(status)) {
01484         UC_SRCE_TRACE("warning: on socket has received fragment = %" PRIx32, (uint32_t)status.code);
01485         ARM_UCS_Http_SetError(status);
01486     }
01487     return status;
01488 }
01489 
01490 /**
01491  * @brief Function drives the download and continues until the buffer is full
01492  *          or the expected amount of data has been downloaded.
01493  */
01494 arm_uc_error_t arm_uc_http_socket_process_frag(void)
01495 {
01496     UC_SRCE_TRACE_ENTRY(">> %s ..", __func__);
01497     ARM_UC_INIT_ERROR(status, ERR_NONE);
01498 
01499     if (context == NULL) {
01500         UC_SRCE_ERR_MSG("error: &context = NULL");
01501         ARM_UC_SET_ERROR(status, SRCE_ERR_UNINITIALIZED);
01502     }
01503     if (ARM_UC_IS_NOT_ERROR(status)) {
01504         /* Fragment or file successfully received */
01505         /* Reset buffers and state */
01506         context->socket_state = STATE_CONNECTED_IDLE;
01507         context->request_buffer = NULL;
01508         context->expected_socket_event = SOCKET_EVENT_UNDEFINED;
01509     }
01510     if (ARM_UC_IS_ERROR(status)) {
01511         UC_SRCE_TRACE("warning: on socket process fragment = %" PRIx32, (uint32_t)status.code);
01512         ARM_UCS_Http_SetError(status);
01513     }
01514     return status;
01515 }
01516 
01517 // CACHE HANDLING.
01518 // ---------------
01519 /**
01520  * @brief Check that the cache state is suitable to supply data as requested.
01521  * @return Whether or not the cache is able to satisfy this request.
01522  */
01523 
01524 bool arm_uc_open_http_socket_matches_request(void)
01525 {
01526     UC_SRCE_TRACE_ENTRY(">> %s ..", __func__);
01527 
01528     bool result = false;
01529 
01530     if (context == NULL) {
01531         UC_SRCE_ERR_MSG("error: &context = NULL");
01532     } else if (context->socket_state != STATE_CONNECTED_IDLE) {
01533         UC_SRCE_TRACE_VERBOSE("!matches: context->socket_state %" PRIu32 " != STATE_CONNECTED_IDLE",
01534                               (uint32_t)context->socket_state);
01535     } else if (context->request_type != context->open_request_type) {
01536         UC_SRCE_TRACE_VERBOSE("!matches: context->request_type %" PRIu32 " != %" PRIu32,
01537                               (uint32_t)context->request_type, (uint32_t)context->open_request_type);
01538     } else if (context->request_offset != context->open_request_offset) {
01539         UC_SRCE_TRACE_VERBOSE("!matches: context->request_offset %" PRIu32 " != %" PRIu32,
01540                               context->request_offset, context->open_request_offset);
01541     } else if (context->open_burst_received >= context->open_burst_expected) {
01542         UC_SRCE_TRACE_VERBOSE("!matches: context->open_burst_remaining == 0");
01543     }
01544     // We need to be VERY sure on what constitutes equality for the uri (e.g. port?)
01545     // It is NOT true that the address of the struct matters, because it could be reconstructed,
01546     //   but keeping the host and path the same.
01547     else if (strcmp((const char *) context->request_uri->host, (const char *) context->open_request_uri->host)
01548              || strcmp((const char *) context->request_uri->path, (const char *) context->open_request_uri->path)) {
01549         UC_SRCE_TRACE_VERBOSE("!matches: context->request_uri %" PRIu32 " != %" PRIu32,
01550                               (uint32_t)context->request_uri, (uint32_t)context->open_request_uri);
01551     } else {
01552         result = true;
01553     }
01554     return result;
01555 }
01556 
01557 // EVENT HANDLING.
01558 // ---------------
01559 
01560 // If the buffer is empty on a read, the processing turnaround time allows for the possibility
01561 //   that there will be data on a subsequent attempt, which this supports. This is only really
01562 //   useful with stream processing of the HTTP socket, rather than manual fragments.
01563 #if defined(TARGET_IS_PC_LINUX)
01564 #define MAX_EMPTY_RECEIVES 8
01565 #else
01566 #define MAX_EMPTY_RECEIVES 2
01567 #endif
01568 
01569 #if defined(ARM_UC_QA_TRACE_ENABLE) && (ARM_UC_QA_TRACE_ENABLE == 1)
01570 uint64_t qaTicksLastAttempt = 0;
01571 #endif
01572 /**
01573  * @brief Show HTTP resume settings if QA trace is enabled.
01574  */
01575 static bool has_displayed_http_resume_settings = false;
01576 static void arm_uc_display_http_resume_settings(arm_uc_resume_t *a_resume_p)
01577 {
01578     if (a_resume_p == NULL) {
01579         UC_SRCE_ERR_MSG("error: a_resume_p = %" PRIxPTR, (uintptr_t)a_resume_p);
01580     } else if (!has_displayed_http_resume_settings) {
01581         has_displayed_http_resume_settings = true;
01582 #if defined(ARM_UC_QA_TRACE_ENABLE) && (ARM_UC_QA_TRACE_ENABLE == 1)
01583         qaTicksLastAttempt = pal_osKernelSysTick();
01584 #endif
01585         UC_QA_TRACE("DNS API v%" PRIu32 " (%s)", PAL_DNS_API_VERSION, PAL_DNS_API_VERSION < 1 ? "sync" : "async");
01586         UC_QA_TRACE("HTTP stream source - download resume settings - actual");
01587         UC_QA_TRACE("exponentiation factor: %" PRIu32
01588                     ", attempt initial delay: %" PRIu32 " ms"
01589                     ", attempt maximum delay: %" PRIu32 " ms"
01590                     ", download maximum time: %" PRIu32 " ms"
01591                     ", interval delay: %" PRIu32 " ms"
01592                     ", interval count: %" PRIu32 "\r\n",
01593                     a_resume_p->exponentiation_factor,
01594                     a_resume_p->attempt_initial_delay,
01595                     a_resume_p->attempt_max_delay,
01596                     a_resume_p->activity_max_time,
01597                     a_resume_p->interval_delay,
01598                     a_resume_p->interval_count);
01599     }
01600 }
01601 
01602 /**
01603  * Keep track of whether or not the HTTP resume settings have been checked for correctness.
01604  *   This occurs only once, which should be sufficient given that they are compile-time
01605  *   values and not modified over the lifetime of the application.
01606  */
01607 static bool has_checked_http_resume_settings = false;
01608 /**
01609  * @brief Check values being passed in for resume-struct initialization (once only).
01610  * @details Note that this struct *cannot* be constant, or the corrections will induce errors.
01611  *          Values are checked on every download cycle to accommodate dynamic value changes.
01612  * @param an_init_p Pointer to structure holding configuration values.
01613  * @return Return if the values were correct as passed in. It is expected that the caller will
01614  *           continue running even if false is returned, given that this function corrects the bad
01615  *           values, however this could be used to log and report an error upstream.
01616  */
01617 static bool arm_uc_http_check_http_resume_parameters(arm_uc_resume_t *a_resume_p)
01618 {
01619     bool result = true;
01620     if (a_resume_p == NULL) {
01621         UC_SRCE_ERR_MSG("error: a_resume_p = %" PRIxPTR, (uintptr_t)a_resume_p);
01622         return false;
01623     }
01624     // low/sensible/high bounds for attempt-initial-delay.
01625     if (a_resume_p->attempt_initial_delay < MIN_INITIAL_ATTEMPT_DELAY_LIMIT) {
01626         a_resume_p->attempt_initial_delay = MIN_INITIAL_ATTEMPT_DELAY_LIMIT;
01627         UC_SRCE_ERR_MSG("HTTP resume initial attempt delay cannot be less than %" PRIu32 " millisecs,"
01628                         " setting to default value of %" PRIu32 " millisecs.",
01629                         (uint32_t)MIN_INITIAL_ATTEMPT_DELAY_LIMIT, a_resume_p->attempt_initial_delay);
01630         result = false;
01631     }
01632     if (a_resume_p->attempt_initial_delay > ADVISABLE_INITIAL_ATTEMPT_DELAY_LIMIT) {
01633         UC_SRCE_TRACE("HTTP resume initial attempt delay should possibly not be more than %" PRIu32 " millisecs.",
01634                       (uint32_t)ADVISABLE_INITIAL_ATTEMPT_DELAY_LIMIT);
01635     }
01636     if (a_resume_p->attempt_initial_delay > MAX_INITIAL_ATTEMPT_DELAY_LIMIT) {
01637         a_resume_p->attempt_initial_delay = MAX_INITIAL_ATTEMPT_DELAY_LIMIT;
01638         UC_SRCE_ERR_MSG("HTTP resume initial attempt delay cannot be more than %" PRIu32 " millisecs,"
01639                         " setting it to that value.",
01640                         a_resume_p->attempt_initial_delay);
01641         result = false;
01642     }
01643     // low/sensible/high bounds for attempt-max-delay.
01644     if (a_resume_p->attempt_max_delay < a_resume_p->attempt_initial_delay) {
01645         a_resume_p->attempt_max_delay = a_resume_p->attempt_initial_delay;
01646         UC_SRCE_ERR_MSG("HTTP resume maximum attempt delay cannot be less than the initial attempt delay,"
01647                         " setting to value of %" PRIu32 " millisecs.",
01648                         a_resume_p->attempt_max_delay);
01649         result = false;
01650     }
01651     if (a_resume_p->attempt_max_delay > ADVISABLE_LONGEST_ATTEMPT_DELAY_LIMIT) {
01652         UC_SRCE_TRACE("HTTP resume maximum attempt delay should possibly not be greater than %" PRIu32 " millisecs.",
01653                       (uint32_t)ADVISABLE_LONGEST_ATTEMPT_DELAY_LIMIT);
01654     }
01655     if (a_resume_p->attempt_max_delay > MAX_LONGEST_ATTEMPT_DELAY_LIMIT) {
01656         a_resume_p->attempt_max_delay = MAX_LONGEST_ATTEMPT_DELAY_LIMIT;
01657         UC_SRCE_ERR_MSG("HTTP resume maximum attempt delay cannot be more than %" PRIu32 " millisecs,"
01658                         " setting it to that value.",
01659                         (uint32_t)MAX_LONGEST_ATTEMPT_DELAY_LIMIT);
01660         result = false;
01661     }
01662     // low/high bounds for activity-max-time.
01663     if (a_resume_p->activity_max_time < a_resume_p->attempt_max_delay) {
01664         a_resume_p->activity_max_time = a_resume_p->attempt_max_delay;
01665         UC_SRCE_ERR_MSG("HTTP resume maximum download time cannot be less than the maximum attempt delay,"
01666                         " setting to value of %" PRIu32 " millisecs.",
01667                         a_resume_p->activity_max_time);
01668         result = false;
01669     }
01670     if (a_resume_p->activity_max_time > MAX_ACTIVITY_TIME_LIMIT) {
01671         a_resume_p->activity_max_time = MAX_ACTIVITY_TIME_LIMIT;
01672         UC_SRCE_ERR_MSG("HTTP resume maximum download time cannot be greater than %" PRIu32 " millisecs,"
01673                         " setting it to that value.",
01674                         (uint32_t)MAX_ACTIVITY_TIME_LIMIT);
01675         result = false;
01676     }
01677     has_checked_http_resume_settings = true;
01678 
01679     return result;
01680 }
01681 
01682 /**
01683  * @brief Ensure the HTTP resume settings are valid.
01684  */
01685 static void arm_uc_http_load_http_resume_parameters(void)
01686 {
01687     resume_http.exponentiation_factor = ARM_UC_HTTP_RESUME_EXPONENTIATION_FACTOR;
01688     resume_http.attempt_initial_delay = ARM_UC_HTTP_RESUME_INITIAL_DELAY_MSECS;
01689     resume_http.attempt_max_delay = ARM_UC_HTTP_RESUME_MAXIMUM_DELAY_MSECS;
01690     resume_http.activity_max_time = ARM_UC_HTTP_RESUME_MAXIMUM_DOWNLOAD_TIME_MSECS;
01691     resume_http.interval_delay = ARM_UC_HTTP_RESUME_INTERVAL_DELAY_MSECS;
01692     resume_http.interval_count = ARM_UC_HTTP_RESUME_NUM_INTERVALS;
01693 }
01694 
01695 /**
01696  * @brief Ensure the HTTP resume settings are valid.
01697  */
01698 static void arm_uc_http_resume_initialize(void)
01699 {
01700     if (!has_checked_http_resume_settings) {
01701         arm_uc_http_load_http_resume_parameters();
01702         arm_uc_http_check_http_resume_parameters(&resume_http);
01703     }
01704 }
01705 
01706 static uint32_t empty_receive = 0;
01707 static bool received_enough = false;
01708 
01709 static char *skip_text_p = "";
01710 #define UC_SRCE_TRACE_SM(s) UC_SRCE_TRACE(s " %s", skip_text_p)
01711 
01712 /**
01713  * @brief PAL socket event handler.
01714  * @details Only handles passing-line code in general, failures handled in subroutines.
01715  * @param unused PAL API doesn't support parameters.
01716  */
01717 // NOTE all external flow of control now takes place in the handler, not scattered around.
01718 // Status codes are updated with each attempted operation, assume good to start.
01719 void arm_uc_http_socket_callback(
01720     uintptr_t an_event)
01721 {
01722     UC_SRCE_TRACE_ENTRY(">> %s (%" PRIx32 ")", __func__, an_event);
01723     ARM_UC_INIT_ERROR(status, ERR_NONE);
01724 
01725     if (context == NULL) {
01726         UC_SRCE_ERR_MSG("error: &context = NULL");
01727         ARM_UC_SET_ERROR(status, SRCE_ERR_UNINITIALIZED);
01728     } else {
01729         do {
01730             an_event &= ~ISR_EVENT_MASK;
01731             switch (an_event) {
01732                 // An event of an undefined type, this is a non-signalling error.
01733                 case SOCKET_EVENT_UNDEFINED:
01734                 UC_SRCE_TRACE_SM("event: undefined, not expected");
01735                     break;
01736 
01737                 // Everything is assumed to have been reset prior to reaching here.
01738                 case SOCKET_EVENT_INITIATE:
01739                     /* Go direct to reading the stream body *if* already open and synchronised */
01740                     /* Else connect socket if not already connected, and start streaming */
01741                     UC_SRCE_TRACE_SM("event: initiate");
01742                     last_http_error_event = UCS_HTTP_EVENT_ERROR;
01743 
01744                     arm_uc_http_resume_initialize();
01745                     arm_uc_resume_initialize(
01746                         &resume_http,
01747                         resume_http.exponentiation_factor,
01748                         resume_http.attempt_initial_delay,
01749                         resume_http.attempt_max_delay,
01750                         resume_http.activity_max_time,
01751                         resume_http.interval_delay,
01752                         resume_http.interval_count,
01753                         on_http_resume_interval,
01754                         on_http_resume_attempt,
01755                         on_http_resume_terminate,
01756                         on_http_resume_error,
01757                         NULL
01758                     );
01759                     arm_uc_resume_start_monitoring(&resume_http);
01760 
01761                     context->resume_socket_phase = SOCKET_EVENT_UNDEFINED;
01762                     status = arm_uc_http_prepare_skip_to_event(SOCKET_EVENT_LOOKUP_START);
01763                     arm_uc_display_http_resume_settings(&resume_http);
01764                     break;
01765 
01766                 case SOCKET_EVENT_LOOKUP_START:
01767                     UC_SRCE_TRACE_SM("event: lookup start");
01768                     context->resume_socket_phase = SOCKET_EVENT_LOOKUP_START;
01769                     if (arm_uc_dns_lookup_is_cached()) {
01770                         status = arm_uc_http_prepare_skip_to_event(SOCKET_EVENT_LOOKUP_DONE);
01771                     } else {
01772                         status = arm_uc_http_get_address_info();
01773                         // The DNS lookup must *always* return BUSY, even if failed,
01774                         //   and then handle the error in the callback handler.
01775                         // There is no expected-event here, it must pass on its own merits.
01776                         status = arm_uc_http_prepare_skip_to_event(SOCKET_EVENT_LOOKUP_WAITING);
01777                     }
01778                     break;
01779 
01780                 case SOCKET_EVENT_LOOKUP_WAITING:
01781                     UC_SRCE_TRACE_SM("event: begin lookup waiting");
01782                     break;
01783 
01784                 case SOCKET_EVENT_LOOKUP_FAILED:
01785                     UC_SRCE_TRACE_SM("event: lookup failed");
01786 #if (PAL_DNS_API_VERSION >= 2)
01787                     // Cancel ongoing asynchronous DNS query (only if non-zero handle).
01788                     arm_uc_http_cancel_dns_lookup();
01789 #endif
01790                     arm_uc_http_clear_dns_cache_fields();
01791                     status = arm_uc_http_prepare_skip_to_event(SOCKET_EVENT_RESUME_WAITING);
01792                     break;
01793 
01794                 case SOCKET_EVENT_LOOKUP_DONE:
01795                     UC_SRCE_TRACE_SM("event: lookup done");
01796                     status = arm_uc_http_prepare_skip_to_event(SOCKET_EVENT_CONNECT_START);
01797                     break;
01798 
01799                 case SOCKET_EVENT_CONNECT_START:
01800                     UC_SRCE_TRACE_SM("event: connect start");
01801                     context->resume_socket_phase = SOCKET_EVENT_CONNECT_START;
01802                     context->number_of_pieces = 0;
01803                     empty_receive = 0;
01804                     if (arm_uc_open_http_socket_matches_request()) {
01805                         status = arm_uc_http_socket_soft_connect();
01806                         if (ARM_UC_IS_NOT_ERROR(status)) {
01807                             // SEPARATED TO AVOID THE SEND STAGES IF POSSIBLE!
01808                             UC_SRCE_TRACE_VERBOSE("    get next frag immediately");
01809                             status = arm_uc_http_prepare_skip_to_event(SOCKET_EVENT_FRAG_START);
01810                         } else {
01811                             UC_SRCE_TRACE_VERBOSE("    error on soft-connect %" PRIx32, status);
01812                         }
01813                     } else {
01814                         status = arm_uc_http_socket_connect();
01815                     }
01816                     break;
01817 
01818                 case SOCKET_EVENT_CONNECT_BLOCKED:
01819                     UC_SRCE_TRACE_SM("event: connect blocked");
01820                     status = arm_uc_http_prepare_skip_to_event(SOCKET_EVENT_RESUME_WAITING);
01821                     break;
01822 
01823                 case SOCKET_EVENT_CONNECT_DONE:
01824                     UC_SRCE_TRACE_SM("event: connect done");
01825                     context->socket_state = STATE_CONNECTED_IDLE;
01826                     status = arm_uc_http_prepare_skip_to_event(SOCKET_EVENT_SEND_START);
01827                     break;
01828 
01829                 case SOCKET_EVENT_SEND_START:
01830                     UC_SRCE_TRACE_SM("event: send start");
01831                     context->resume_socket_phase = SOCKET_EVENT_SEND_START;
01832                     status = arm_uc_http_socket_send_request();
01833                     break;
01834 
01835                 case SOCKET_EVENT_SEND_BLOCKED:
01836                     UC_SRCE_TRACE_SM("event: send blocked");
01837                     context->expected_socket_event = SOCKET_EVENT_SEND_DONE;
01838                     status = arm_uc_http_prepare_skip_to_event(SOCKET_EVENT_RESUME_WAITING);
01839                     break;
01840 
01841                 case SOCKET_EVENT_SEND_DONE:
01842                     // Guard this from being invoked by a second PAL-generated event.
01843                     // If the state machine has already passed on, just ignore the event.
01844                     if (context->resume_socket_phase != SOCKET_EVENT_SEND_START) {
01845                         break;
01846                     }
01847                     /* Request has been sent, receive response */
01848                     UC_SRCE_TRACE_SM("event: send done");
01849                     status = arm_uc_http_prepare_skip_to_event(SOCKET_EVENT_HEADER_START);
01850                     break;
01851 
01852                 case SOCKET_EVENT_HEADER_START:
01853                     UC_SRCE_TRACE_SM("event: header start");
01854                     context->resume_socket_phase = SOCKET_EVENT_HEADER_START;
01855                     status = arm_uc_http_prepare_skip_to_event(SOCKET_EVENT_HEADER_MORE);
01856                     break;
01857 
01858                 case SOCKET_EVENT_HEADER_MORE:
01859                     UC_SRCE_TRACE_SM("event: header more");
01860                     status = arm_uc_http_socket_receive();
01861                     switch (ARM_UC_GET_ERROR(status)) {
01862                         case SRCE_ERR_BUSY:
01863                             // Nothing to read from the socket (it returned WOULD_BLOCK),
01864                             //   so try again a few times, until must give up and wait,
01865                             UC_SRCE_TRACE_VERBOSE("event: empty header receive");
01866                             if (++empty_receive < MAX_EMPTY_RECEIVES) {
01867                                 status = arm_uc_http_install_app_event(SOCKET_EVENT_HEADER_MORE);
01868                             } else {
01869                                 UC_SRCE_TRACE("event: awaiting non-empty header");
01870                                 // Just wait for notification, the rest hasn't arrived yet.
01871                                 context->expected_socket_event = SOCKET_EVENT_HEADER_MORE;
01872                                 ARM_UC_SET_ERROR(status, ERR_NONE);
01873                             }
01874                             break;
01875                         case ERR_NONE:
01876                             empty_receive = 0;
01877                             received_enough = false;
01878                             status = arm_uc_http_socket_has_received_header(&received_enough);
01879                             if (ARM_UC_GET_ERROR(status) == SRCE_ERR_ABORT) {
01880                                 // If an ABORT error is returned, terminate the whole resume cycle.
01881                                 UC_SRCE_ERR_MSG("event: aborted, unable to receive header");
01882                                 status = arm_uc_http_prepare_skip_to_event(SOCKET_EVENT_RESUME_TERMINATED);
01883                             } else if (ARM_UC_IS_NOT_ERROR(status)) {
01884                                 if (received_enough) {
01885                                     status = arm_uc_http_socket_process_header();
01886                                     if (ARM_UC_IS_NOT_ERROR(status)) {
01887                                         status = arm_uc_http_prepare_skip_to_event(SOCKET_EVENT_HEADER_DONE);
01888                                     } else if (ARM_UC_GET_ERROR(status) == SRCE_ERR_ABORT) {
01889                                         // If an ABORT error is returned, terminate the whole resume cycle.
01890                                         UC_SRCE_TRACE("event: aborted with bad header");
01891                                         status = arm_uc_http_prepare_skip_to_event(SOCKET_EVENT_RESUME_TERMINATED);
01892                                     } else if (ARM_UC_GET_ERROR(status) == SRCE_ERR_BUSY) {
01893                                         // Special case here of a redirecting header.
01894                                         // Do nothing, just wait for the newly scheduled event.
01895                                         ARM_UC_SET_ERROR(status, ERR_NONE);
01896                                     }
01897                                 } else {
01898                                     status = arm_uc_http_install_app_event(SOCKET_EVENT_HEADER_MORE);
01899                                 }
01900                             }
01901                             break;
01902                         default:
01903                             ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
01904                             break;
01905                     }
01906                     break;
01907 
01908                 case SOCKET_EVENT_HEADER_BLOCKED:
01909                     // Should never occur, handler only goes between more and done
01910                     // (because this is managed by local code, not the socket.)
01911                     UC_SRCE_TRACE_SM("event: header blocked");
01912                     ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
01913                     break;
01914 
01915                 case SOCKET_EVENT_HEADER_DONE:
01916                     UC_SRCE_TRACE_SM("event: header done. Reset resume engine");
01917                     arm_uc_resume_resynch_monitoring(&resume_http);
01918                     empty_receive = 0;
01919                     status = arm_uc_http_prepare_skip_to_event(SOCKET_EVENT_FRAG_START);
01920                     break;
01921 
01922                 case SOCKET_EVENT_FRAG_START:
01923                     UC_SRCE_TRACE_SM("event: frag start");
01924                     context->resume_socket_phase = SOCKET_EVENT_FRAG_MORE;
01925                     context->expected_socket_event = SOCKET_EVENT_FRAG_MORE;
01926                     status = arm_uc_http_prepare_skip_to_event(SOCKET_EVENT_FRAG_MORE);
01927                     break;
01928 
01929                 case SOCKET_EVENT_FRAG_MORE:
01930                     // Guard this from being invoked by a second PAL-generated event.
01931                     // If the state machine has already passed on, just ignore the event.
01932                     if (context->resume_socket_phase != SOCKET_EVENT_FRAG_MORE) {
01933                         break;
01934                     }
01935                     UC_SRCE_TRACE_SM("event: frag more");
01936                     status = arm_uc_http_socket_receive();
01937                     switch (ARM_UC_GET_ERROR(status)) {
01938                         case SRCE_ERR_BUSY:
01939                             // Nothing to read from the socket (it returned WOULD_BLOCK),
01940                             //   so carry on in the same state, waiting for a callback from the socket.
01941                             // The resume-monitor should catch timeout errors.
01942                             // mbedOS tries again because implementations are slow enough to benefit.
01943                             UC_SRCE_TRACE_VERBOSE("event: empty fragment receive");
01944                             if (++empty_receive < MAX_EMPTY_RECEIVES) {
01945                                 status = arm_uc_http_install_app_event(SOCKET_EVENT_FRAG_MORE);
01946                             } else {
01947                                 UC_SRCE_TRACE_VERBOSE("event: max empty fragment receives");
01948                                 // just wait for notification, the rest hasn't arrived yet.
01949                                 context->expected_socket_event = SOCKET_EVENT_FRAG_MORE;
01950                                 ARM_UC_SET_ERROR(status, ERR_NONE);
01951                             }
01952                             break;
01953                         case ERR_NONE:
01954                             UC_SRCE_TRACE_VERBOSE("http socket receive event: no error. Reset resume engine");
01955                             arm_uc_resume_resynch_monitoring(&resume_http);
01956                             ++context->number_of_pieces;
01957                             empty_receive = 0;
01958 
01959                             received_enough = false;
01960                             status = arm_uc_http_socket_has_received_frag(&received_enough);
01961                             if (ARM_UC_IS_NOT_ERROR(status)) {
01962                                 if (received_enough) {
01963                                     status = arm_uc_http_socket_process_frag();
01964                                     if (ARM_UC_IS_NOT_ERROR(status)) {
01965                                         status = arm_uc_http_prepare_skip_to_event(SOCKET_EVENT_FRAG_DONE);
01966                                     }
01967                                 } else {
01968                                     status = arm_uc_http_install_app_event(SOCKET_EVENT_FRAG_MORE);
01969                                 }
01970                             }
01971                             break;
01972                         default:
01973                             ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
01974                             break;
01975                     }
01976                     break;
01977 
01978                 case SOCKET_EVENT_FRAG_BLOCKED:
01979                     // Should never occur, handler only goes between more and done.
01980                     // (because this is managed by local code, not the socket.)
01981                     UC_SRCE_TRACE_SM("event: frag blocked");
01982                     ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
01983                     break;
01984 
01985                 case SOCKET_EVENT_FRAG_DONE:
01986                     UC_SRCE_TRACE_SM("event: frag done");
01987                     arm_uc_resume_end_monitoring(&resume_http);
01988                     context->resume_socket_phase = SOCKET_EVENT_UNDEFINED;
01989                     context->expected_socket_event = SOCKET_EVENT_UNDEFINED;
01990                     ARM_UC_PostCallback(NULL, context->callback_handler, UCS_HTTP_EVENT_DOWNLOAD);
01991 
01992                     // Socket has been mangled by bad link, give it a chance to clear itself up.
01993                     if (frags_per_burst && context->number_of_pieces >= frags_per_burst) {
01994 
01995                         arm_uc_http_socket_close();
01996                     }
01997                     break;
01998 
01999                 case SOCKET_EVENT_TIMER_FIRED:
02000                     UC_SRCE_TRACE_SM("event: warning: socket timer fired");
02001                     break;
02002 
02003                 case SOCKET_EVENT_RESUME_WAITING:
02004                     UC_SRCE_TRACE_SM("event: begin resume waiting");
02005                     // Just a marker event to make code and trace more readable.
02006                     // The state machine will wait here until resume kicks in and makes things happen.
02007                     break;
02008 
02009                 case SOCKET_EVENT_RESUME_INTERVAL:
02010                     UC_SRCE_TRACE_SM("event: http-socket resume interval - timeout");
02011                     // The socket API isn't reliable, so we use the resume interval timer to
02012                     //   artificially propose that the expected event has in fact arrived.
02013                     // This should be set to be longer than the event would take if not lost.
02014                     if (context->expected_socket_event != 0) {
02015                         status = arm_uc_http_prepare_skip_to_event(context->expected_socket_event);
02016                         context->expected_socket_event = 0;
02017                     }
02018                     break;
02019 
02020                 case SOCKET_EVENT_RESUME_ATTEMPT:
02021                     // Resume will attempt to pick up where the last events were happening.
02022                     UC_SRCE_TRACE_VERBOSE("event: http-socket resume attempt %s", skip_text_p);
02023                     // Now try to actually get a fragment for the resumption.
02024                     UC_SRCE_TRACE_VERBOSE("with resource state currently");
02025                     UC_SRCE_TRACE_VERBOSE("     offset %" PRIu32, context->request_offset);
02026                     UC_SRCE_TRACE_VERBOSE("     host %s", context->request_uri->host);
02027                     UC_SRCE_TRACE_VERBOSE("     path %s", context->request_uri->path);
02028                     UC_SRCE_TRACE_VERBOSE("     filled %" PRIu32, context->request_buffer->size);
02029                     UC_SRCE_TRACE_VERBOSE("next attempt in %" PRIu32 " secs", resume_http.expected_delay / 1000);
02030                     UC_SRCE_TRACE_VERBOSE("  (sum total is %" PRIu32 " secs)", resume_http.sum_total_period / 1000);
02031                     UC_SRCE_TRACE_VERBOSE("  (max total is %" PRIu32 " secs)", resume_http.activity_max_time / 1000);
02032 
02033 #if defined(ARM_UC_QA_TRACE_ENABLE) && (ARM_UC_QA_TRACE_ENABLE == 1)
02034                     uint64_t qaTicksNow = pal_osKernelSysTick();
02035                     UC_QA_TRACE("HTTP download-resume attempt after %" PRIu32" seconds delay.",
02036                                 (uint32_t)(pal_osKernelSysMilliSecTick(qaTicksNow - qaTicksLastAttempt)/1000));
02037                     qaTicksLastAttempt = qaTicksNow;
02038                     UC_QA_TRACE("Next attempt in %" PRIu32" seconds.\r\n", resume_http.jitter_delay / 1000);
02039 #endif
02040 
02041                     // Now decide just how aggressive to be about resuming.
02042                     // Every attempt closes the socket, every second attempt flushes the DNS cache.
02043 #if (PAL_DNS_API_VERSION >= 2)
02044                     // Cancel ongoing asynchronous DNS query (only if non-zero handle).
02045                     arm_uc_http_cancel_dns_lookup();
02046 #endif
02047                     if (resume_http.num_attempts != 0) {
02048                         arm_uc_http_socket_close();
02049                         context->resume_socket_phase = SOCKET_EVENT_CONNECT_START;
02050                         if (resume_http.num_attempts % 2 == 0) {
02051                             arm_uc_http_clear_dns_cache_fields();
02052                         }
02053                     }
02054                     if (!arm_uc_dns_lookup_is_cached()) {
02055                         context->resume_socket_phase = SOCKET_EVENT_LOOKUP_START;
02056                     }
02057                     if (context->resume_socket_phase != 0) {
02058                         status = arm_uc_http_prepare_skip_to_event(context->resume_socket_phase);
02059                         context->resume_socket_phase = SOCKET_EVENT_UNDEFINED;
02060                     }
02061                     break;
02062 
02063                 case SOCKET_EVENT_RESUME_TERMINATED:
02064                     // Note that this case will leave the switch with an error,
02065                     //   which will invoke clean-up as appropriate.
02066 
02067 #if (PAL_DNS_API_VERSION >= 2)
02068                     // Cancel ongoing asynchronous DNS query (only if non-zero handle).
02069                     arm_uc_http_cancel_dns_lookup();
02070 #endif
02071                     arm_uc_resume_end_monitoring(&resume_http);
02072 
02073                     UC_QA_TRACE("\r\nHTTP download-resume terminating now.\r\n");
02074                     UC_SRCE_ERR_MSG("event handler: failed with %" PRIu32 ", %" PRIx32 ", %s",
02075                                     an_event, (uint32_t)status.code, ARM_UC_err2Str(status));
02076                     arm_uc_http_socket_fatal_error(last_http_error_event);
02077                     break;
02078 
02079                 case SOCKET_EVENT_RESUME_ERROR:
02080                     UC_SRCE_TRACE_SM("event: http-socket resume errored");
02081 #if (PAL_DNS_API_VERSION >= 2)
02082                     // Cancel ongoing asynchronous DNS query (only if non-zero handle).
02083                     arm_uc_http_cancel_dns_lookup();
02084 #endif
02085                     arm_uc_resume_end_monitoring(&resume_http);
02086                     arm_uc_http_socket_fatal_error(UCS_HTTP_EVENT_ERROR);
02087                     break;
02088 
02089                 default:
02090                     UC_SRCE_TRACE_SM("error: unknown event type!");
02091 #if (PAL_DNS_API_VERSION >= 2)
02092                     // Cancel ongoing asynchronous DNS query (only if non-zero handle).
02093                     arm_uc_http_cancel_dns_lookup();
02094 #endif
02095                     status = arm_uc_http_prepare_skip_to_event(SOCKET_EVENT_RESUME_WAITING);
02096                     break;
02097             }
02098             if (ARM_UC_IS_ERROR(status)) {
02099                 status = arm_uc_http_prepare_skip_to_event(SOCKET_EVENT_RESUME_WAITING);
02100                 ARM_UCS_Http_SetError(status);
02101             }
02102             an_event = 0;
02103             // Check if there is a skip-to-event to handle.
02104             skip_text_p = skip_to_event ? "auto" : "";
02105             if (skip_to_event != 0) {
02106                 an_event = skip_to_event;
02107                 skip_to_event = 0;
02108             }
02109         } while (an_event != 0);
02110     }
02111 }
02112 
02113 /**
02114  * @brief Callback handler for PAL socket events. Callbacks go through the task
02115  *        queue because we don't know what context we are running from.
02116  * @details Note that we don't do printing inside here, because we could be running
02117  *            from inside an interrupt context.
02118  */
02119 arm_uc_error_t arm_uc_http_install_event(
02120     arm_uc_http_socket_event_t an_event)
02121 {
02122     ARM_UC_INIT_ERROR(status, ERR_NONE);
02123 
02124     if (context == NULL) {
02125         ARM_UC_SET_ERROR(status, SRCE_ERR_FAILED);
02126     } else {
02127         ARM_UC_PostCallback(NULL, arm_uc_http_socket_callback, an_event);
02128     }
02129     if (ARM_UC_IS_ERROR(status)) {
02130         ARM_UCS_Http_SetError(status);
02131     }
02132     return status;
02133 }
02134 
02135 static inline arm_uc_error_t arm_uc_http_install_isr_event(
02136     arm_uc_http_socket_event_t an_event)
02137 {
02138     return arm_uc_http_install_event(an_event | ISR_EVENT_MASK);
02139 }
02140 
02141 static inline arm_uc_error_t arm_uc_http_install_app_event(
02142     arm_uc_http_socket_event_t an_event)
02143 {
02144     UC_SRCE_TRACE_VERBOSE(">> %s installed app-event %" PRIx32 " ..", __func__, an_event);
02145     return arm_uc_http_install_event(an_event);
02146 }
02147 
02148 /**
02149  * @brief Filter socket events before installing as ISR events.
02150  */
02151 void arm_uc_http_socket_isr(
02152     void *an_event)
02153 {
02154     if (context != NULL) {
02155         // Only allow a single typeless socket event at a time.
02156         if (context->expected_socket_event == SOCKET_EVENT_UNDEFINED) {
02157             // Drop this event.
02158         } else {
02159             // Expected as the next event generated by the socket.
02160             arm_uc_http_socket_event_t event = context->expected_socket_event;
02161             ARM_UC_INIT_ERROR(status, ERR_NONE);
02162             status = arm_uc_http_install_isr_event(event);
02163             if (ARM_UC_IS_NOT_ERROR(status)) {
02164                 context->expected_socket_event = SOCKET_EVENT_UNDEFINED;
02165             }
02166         }
02167     }
02168 }
02169 
02170 /**
02171  * @brief Callback handler for the asynchronous DNS resolver.
02172  *        Callbacks go through the task queue because we don't know
02173  *        what context we are running from.
02174  */
02175 #if (PAL_DNS_API_VERSION >= 2)
02176 void arm_uc_dns_callback_handler(
02177     const char *url,
02178     palSocketAddress_t *address,
02179     palStatus_t pal_status,
02180     void *argument)
02181 #else
02182 void arm_uc_dns_callback_handler(
02183     const char *url,
02184     palSocketAddress_t *address,
02185     palSocketLength_t *address_length,
02186     palStatus_t pal_status,
02187     void *argument)
02188 #endif
02189 {
02190     (void) url;
02191     (void) address;
02192 #if (PAL_DNS_API_VERSION < 2)
02193     (void) address_length;
02194 #endif
02195     (void) argument;
02196 
02197     if (context != NULL) {
02198         arm_uc_http_socket_event_t event;
02199 #if (PAL_DNS_API_VERSION >= 2)
02200         arm_uc_dns_query_handle = 0;
02201 #endif
02202         /* accept the DNS callback event only if we were expecting it. */
02203         if (expecting_dns_callback) {
02204             expecting_dns_callback = false;
02205             UC_SRCE_TRACE(">> %s, PAL DNS lookup returned", __func__);
02206             if (pal_status == PAL_SUCCESS) {
02207                 uint64_t data = 0;
02208                 int index = 0;
02209                 for (data = 0, index = 0; index < 8; ++index) {
02210                     data = (data << 8) + address->addressData[index];
02211                 }
02212                 UC_SRCE_TRACE("           with type  %" PRIu16, address->addressType);
02213                 UC_SRCE_TRACE("           with addr  %" PRIx64, data);
02214 #if (PAL_DNS_API_VERSION < 2)
02215                 UC_SRCE_TRACE("           with space %" PRIu32, *address_length);
02216 #endif
02217                 event = ((arm_uc_http_socket_event_t) SOCKET_EVENT_LOOKUP_DONE);
02218             } else {
02219                 /* Clear the address-related fields to signal an error */
02220                 UC_SRCE_ERR_MSG("PAL DNS lookup failed %" PRIx32, (uint32_t)pal_status);
02221                 event = ((arm_uc_http_socket_event_t) SOCKET_EVENT_LOOKUP_FAILED);
02222             }
02223             ARM_UC_INIT_ERROR(status, ERR_NONE);
02224             status = arm_uc_http_install_isr_event(event);
02225             // Only clear the resume expected-event if this has installed correctly.
02226             // Otherwise rely on the fake resume-interval-event to take care of things.
02227             if (ARM_UC_IS_NOT_ERROR(status)) {
02228                 context->expected_socket_event = SOCKET_EVENT_UNDEFINED;
02229             }
02230         } else {
02231             UC_SRCE_TRACE("unexpected %s", __func__);
02232         }
02233     }
02234 }
02235 
02236 void arm_uc_http_socket_end_resume(void)
02237 {
02238     arm_uc_resume_end_monitoring(&resume_http);
02239 }
02240 
02241 
02242 #endif // ARM_UC_FEATURE_FW_SOURCE_HTTP