Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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
Generated on Mon Aug 29 2022 19:53:38 by
