Simulated product dispenser
Fork of mbed-cloud-workshop-connect-HTS221 by
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 "arm_uc_http_socket_private.h" 00020 00021 #include "arm_uc_socket_help.h" 00022 00023 #include <pal.h> 00024 00025 #include <stdio.h> 00026 #include <string.h> 00027 #include <inttypes.h> 00028 00029 #if !defined(ARM_UC_SOCKET_TIMEOUT_MS) 00030 #define ARM_UC_SOCKET_TIMEOUT_MS 10000 /* 10 seconds */ 00031 #endif 00032 00033 /* Pointer to struct containing all global variables. 00034 Can be dynamically allocated and deallocated. 00035 */ 00036 static arm_uc_http_socket_context_t* context = NULL; 00037 00038 /****************************************************************************** 00039 * Internal helpers for DNS resolution 00040 * These functions are used to implement DNS resolution in two variants: 00041 * asynchronous (MBED_CONF_MBED_CLIENT_DNS_USE_THREAD == 1) or 00042 * synchronous (otherwise). */ 00043 00044 #if MBED_CONF_MBED_CLIENT_DNS_USE_THREAD /* use asychronous DNS calls */ 00045 00046 #define arm_uc_get_address_info pal_getAddressInfoAsync 00047 00048 static arm_uc_error_t arm_uc_start_dns_timer() 00049 { 00050 arm_uc_error_t result = (arm_uc_error_t){ SRCE_ERR_FAILED }; 00051 00052 if (context) 00053 { 00054 palStatus_t pal_inner = pal_osTimerStart(context->timeout_timer_id, 00055 ARM_UC_SOCKET_TIMEOUT_MS); 00056 if (pal_inner != PAL_SUCCESS) 00057 { 00058 UC_SRCE_ERR_MSG("Start socket timeout timer failed pal status: 0x%" PRIu32, 00059 (uint32_t)pal_inner); 00060 arm_uc_socket_close(); 00061 } 00062 else 00063 { 00064 result.code = SRCE_ERR_NONE; 00065 } 00066 } 00067 return result; 00068 } 00069 00070 static arm_uc_error_t arm_uc_stop_dns_timer() 00071 { 00072 arm_uc_error_t result = (arm_uc_error_t){ SRCE_ERR_FAILED }; 00073 00074 if (context) 00075 { 00076 palStatus_t pal_inner = pal_osTimerStop(context->timeout_timer_id); 00077 if (pal_inner != PAL_SUCCESS) 00078 { 00079 UC_SRCE_ERR_MSG("pal_osTimerStop returned 0x%" PRIX32, 00080 (uint32_t) pal_inner); 00081 } 00082 else 00083 { 00084 result.code = SRCE_ERR_NONE; 00085 } 00086 } 00087 return result; 00088 } 00089 00090 #else /* use synchronous DNS calls */ 00091 00092 static palStatus_t arm_uc_get_address_info(const char* url, palSocketAddress_t* address, 00093 palSocketLength_t* address_length, 00094 palGetAddressInfoAsyncCallback_t callback, 00095 void* argument) 00096 { 00097 /* Run the synchronous DNS request and call the callback 00098 immediately after the request is done */ 00099 palStatus_t pal_inner = pal_getAddressInfo(url, address, address_length); 00100 /* Call the callback with the result of pal_getAddressInfo. 00101 The callback will examine the value of pal_inner and act 00102 accordingly (see arm_uc_dns_callback below). */ 00103 callback(url, address, address_length, pal_inner, argument); 00104 /* Always return PAL_SUCCESS so that the caller can continue 00105 execution. The actual check for success/failure happens 00106 in the callback (see the comment above). */ 00107 return PAL_SUCCESS; 00108 } 00109 00110 /* Timers are not used for synchronous DNS calls, 00111 since the synchronous call can't be interrupted */ 00112 static arm_uc_error_t arm_uc_start_dns_timer() 00113 { 00114 return (arm_uc_error_t){ SRCE_ERR_NONE }; 00115 } 00116 00117 static arm_uc_error_t arm_uc_stop_dns_timer() 00118 { 00119 return (arm_uc_error_t){ SRCE_ERR_NONE }; 00120 } 00121 00122 #endif // MBED_CONF_MBED_CLIENT_DNS_USE_THREAD 00123 00124 /*****************************************************************************/ 00125 00126 /** 00127 * @brief Initialize Http module. 00128 * @details A memory struct is passed as well as a function pointer for event 00129 * handling. 00130 * 00131 * @param context Struct holding all global variables. 00132 * @param handler Event handler for signaling when each operation is complete. 00133 * @return Error code. 00134 */ 00135 arm_uc_error_t arm_uc_socket_initialize(arm_uc_http_socket_context_t* _context, 00136 void (*handler)(uint32_t)) 00137 { 00138 UC_SRCE_TRACE("arm_uc_socket_initialize"); 00139 00140 /* default return value */ 00141 arm_uc_error_t result = (arm_uc_error_t){ SRCE_ERR_FAILED }; 00142 00143 /* save global context */ 00144 context = _context; 00145 00146 if (context) 00147 { 00148 /* initialize global variables */ 00149 context->callback_handler = handler; 00150 00151 context->request_uri = NULL; 00152 context->request_buffer = NULL; 00153 context->request_offset = 0; 00154 context->request_type = RQST_TYPE_NONE; 00155 00156 context->socket_state = STATE_DISCONNECTED; 00157 context->expected_event = SOCKET_EVENT_UNDEFINED; 00158 context->expected_remaining = 0; 00159 00160 context->socket = NULL; 00161 00162 context->isr_callback_counter = 0; 00163 00164 context->timeout_timer_id = 0; 00165 00166 context->cache_address.addressType = 0; 00167 memset(context->cache_address.addressData, 0, PAL_NET_MAX_ADDR_SIZE); 00168 context->cache_address_length = 0; 00169 00170 /* set return value to success */ 00171 result = (arm_uc_error_t){ SRCE_ERR_NONE }; 00172 } 00173 00174 return result; 00175 } 00176 00177 /** 00178 * @brief Resets HTTP socket to uninitialized state and clears memory struct. 00179 * @details HTTP sockets must be initialized again before use. 00180 * @return Error code. 00181 */ 00182 arm_uc_error_t arm_uc_socket_terminate() 00183 { 00184 /* default return value */ 00185 arm_uc_error_t result = (arm_uc_error_t){ SRCE_ERR_FAILED }; 00186 00187 if (context) 00188 { 00189 /* close socket */ 00190 arm_uc_socket_close(); 00191 00192 /* reset all global variables */ 00193 context->request_uri = NULL; 00194 context->request_buffer = NULL; 00195 context->request_offset = 0; 00196 context->request_type = RQST_TYPE_NONE; 00197 00198 context->socket_state = STATE_DISCONNECTED; 00199 context->expected_event = SOCKET_EVENT_UNDEFINED; 00200 context->expected_remaining = 0; 00201 00202 context->socket = NULL; 00203 00204 context = NULL; 00205 00206 result = (arm_uc_error_t){ SRCE_ERR_NONE }; 00207 } 00208 00209 return result; 00210 } 00211 00212 /** 00213 * @brief Get resource at URI. 00214 * @details Download resource at URI from given offset and store in buffer. 00215 * Events are generated when download finish or on error 00216 * 00217 * @param uri Pointer to structure with resource location. 00218 * @param buffer Pointer to structure with buffer location, maxSize, and size. 00219 * @param offset Offset in resource to begin download from. 00220 * @param type Indicate what type of request that was initiated. 00221 * @return Error code. 00222 */ 00223 arm_uc_error_t arm_uc_socket_get(arm_uc_uri_t* uri, 00224 arm_uc_buffer_t* buffer, 00225 uint32_t offset, 00226 arm_uc_rqst_t type) 00227 { 00228 UC_SRCE_TRACE("arm_uc_socket_get"); 00229 00230 /* default return value */ 00231 arm_uc_error_t result = (arm_uc_error_t){ SRCE_ERR_INVALID_PARAMETER }; 00232 00233 /* check for NULL pointers */ 00234 if (uri && 00235 uri->scheme && 00236 uri->host && 00237 uri->path && 00238 buffer && 00239 buffer->ptr && 00240 context) 00241 { 00242 /* parameters are valid */ 00243 result.code = SRCE_ERR_NONE; 00244 00245 /* store request */ 00246 context->request_uri = uri; 00247 context->request_buffer = buffer; 00248 context->request_offset = offset; 00249 context->request_type = type; 00250 00251 /* clear buffer */ 00252 context->request_buffer->size = 0; 00253 00254 UC_SRCE_TRACE("Socket State: %d", context->socket_state); 00255 00256 /* connect socket if not already connected */ 00257 result = arm_uc_socket_connect(); 00258 } 00259 00260 return result; 00261 } 00262 00263 /** 00264 * @brief Connect to server set in the global URI struct. 00265 * @details Connecting generates a socket event, which automatically processes 00266 * the request passed in arm_uc_socket_get. If a DNS request must 00267 * be made, this call initiates an asynchronous DNS request. After 00268 * the request is done, the connection process will be resumed in 00269 * arm_uc_socket_finish_connect(). 00270 * @return Error code. 00271 */ 00272 arm_uc_error_t arm_uc_socket_connect() 00273 { 00274 /* default return value */ 00275 arm_uc_error_t result = (arm_uc_error_t){ SRCE_ERR_FAILED }; 00276 00277 /* NULL pointer check */ 00278 if (context && context->request_uri) 00279 { 00280 if (context->socket_state == STATE_DISCONNECTED) 00281 { 00282 result = (arm_uc_error_t){ SRCE_ERR_NONE }; 00283 00284 /* create socket timeout timer */ 00285 palStatus_t pal_inner = pal_osTimerCreate(arm_uc_timeout_timer_callback, 00286 NULL, 00287 palOsTimerOnce, 00288 &context->timeout_timer_id); 00289 00290 if (pal_inner != PAL_SUCCESS) 00291 { 00292 UC_SRCE_ERR_MSG("socket timeout timer creation failed pal status: 0x%" PRIu32, 00293 (uint32_t)pal_inner); 00294 arm_uc_socket_close(); 00295 result = (arm_uc_error_t){ SRCE_ERR_FAILED }; 00296 } 00297 else 00298 { 00299 /* start socket timeout timer */ 00300 result = arm_uc_start_dns_timer(); 00301 } 00302 if (result.code == SRCE_ERR_NONE) 00303 { 00304 /* initiate DNS lookup */ 00305 pal_inner = arm_uc_get_address_info(context->request_uri->host, 00306 &context->cache_address, 00307 &context->cache_address_length, 00308 arm_uc_dns_callback, 00309 NULL); 00310 00311 if (pal_inner != PAL_SUCCESS) 00312 { 00313 UC_SRCE_ERR_MSG("pal_getAddressInfoAsync (DNS) failed pal status: 0x%" PRIu32, 00314 (uint32_t)pal_inner); 00315 arm_uc_socket_close(); 00316 result = (arm_uc_error_t){ SRCE_ERR_INVALID_PARAMETER }; 00317 } 00318 else 00319 { 00320 UC_SRCE_TRACE("Initiated DNS lookup"); 00321 } 00322 } 00323 } 00324 else if (context->socket_state == STATE_CONNECTED_IDLE) /* already connected */ 00325 { 00326 /* Socket already connected, progress state machine */ 00327 palStatus_t pal_inner = pal_osTimerStart(context->timeout_timer_id, 00328 ARM_UC_SOCKET_TIMEOUT_MS); 00329 00330 if (pal_inner != PAL_SUCCESS) 00331 { 00332 UC_SRCE_ERR_MSG("Start socket timeout timer failed pal status: 0x%" PRIu32, 00333 (uint32_t)pal_inner); 00334 arm_uc_socket_close(); 00335 return (arm_uc_error_t){ SRCE_ERR_FAILED }; 00336 } 00337 00338 UC_SRCE_TRACE("Socket already connected, progress state machine"); 00339 context->expected_event = SOCKET_EVENT_CONNECT_DONE; 00340 result = (arm_uc_error_t){ SRCE_ERR_NONE }; 00341 arm_uc_socket_isr(NULL); 00342 } 00343 else /* socket busy */ 00344 { 00345 UC_SRCE_TRACE("Socket Busy"); 00346 result = (arm_uc_error_t){ SRCE_ERR_BUSY }; 00347 } 00348 } 00349 00350 UC_SRCE_TRACE("arm_uc_socket_connect returning %s", ARM_UC_err2Str(result)); 00351 00352 return result; 00353 } 00354 00355 /** 00356 * @brief Finishes connecting to the server requested in a previous call to 00357 * arm_uc_socket_connect(). 00358 * @details This function is called after the DNS resolution for the host 00359 * requested in arm_uc_socket_get() above is done. It finishes the 00360 * connection process by creating a socket and connecting it. 00361 * @return Error code. 00362 */ 00363 arm_uc_error_t arm_uc_socket_finish_connect() 00364 { 00365 /* default return value */ 00366 arm_uc_error_t result = (arm_uc_error_t){ SRCE_ERR_FAILED }; 00367 00368 /* NULL pointer check */ 00369 if (context && context->request_uri) 00370 { 00371 result = (arm_uc_error_t){ SRCE_ERR_NONE }; 00372 00373 UC_SRCE_TRACE("socket: address type is: %u", context->cache_address.addressType); 00374 /* create new async PAL socket */ 00375 palStatus_t pal_inner = pal_asynchronousSocket((palSocketDomain_t )context->cache_address.addressType, 00376 PAL_SOCK_STREAM, 00377 true, 00378 0, 00379 arm_uc_socket_isr, 00380 &context->socket); 00381 00382 if (pal_inner != PAL_SUCCESS) 00383 { 00384 UC_SRCE_ERR_MSG("socket creation failed with pal status: 0x%" PRIX32, 00385 (uint32_t) pal_inner); 00386 arm_uc_socket_close(); 00387 result = (arm_uc_error_t){ SRCE_ERR_FAILED }; 00388 } 00389 else 00390 { 00391 UC_SRCE_TRACE("socket: create success"); 00392 } 00393 00394 /* start socket timeout timer */ 00395 if (result.code == SRCE_ERR_NONE) 00396 { 00397 pal_inner = pal_osTimerStart(context->timeout_timer_id, 00398 ARM_UC_SOCKET_TIMEOUT_MS); 00399 00400 if (pal_inner != PAL_SUCCESS) 00401 { 00402 UC_SRCE_ERR_MSG("Start socket timeout timer failed"); 00403 arm_uc_socket_close(); 00404 result = (arm_uc_error_t){ SRCE_ERR_FAILED }; 00405 } 00406 } 00407 00408 /* convert URI to PAL address if cache is not empty */ 00409 if ((result.code == SRCE_ERR_NONE) && 00410 (context->cache_address_length != 0)) 00411 { 00412 /* set PAL port */ 00413 pal_inner = pal_setSockAddrPort(&context->cache_address, 00414 context->request_uri->port); 00415 00416 if (pal_inner != PAL_SUCCESS) 00417 { 00418 UC_SRCE_ERR_MSG("pal_setSockAddrPort returned: 0x%" PRIX32, 00419 (uint32_t) pal_inner); 00420 arm_uc_socket_close(); 00421 result = (arm_uc_error_t){ SRCE_ERR_INVALID_PARAMETER }; 00422 } 00423 } 00424 00425 /* connect to server */ 00426 if (result.code == SRCE_ERR_NONE) 00427 { 00428 pal_inner = pal_connect(context->socket, 00429 &context->cache_address, 00430 context->cache_address_length); 00431 UC_SRCE_TRACE("pal_connect returned: 0x%" PRIX32, 00432 (uint32_t) pal_inner); 00433 00434 if (pal_inner == PAL_SUCCESS) /* synchronous finish */ 00435 { 00436 context->socket_state = STATE_CONNECTED_IDLE; 00437 context->expected_event = SOCKET_EVENT_CONNECT_DONE; 00438 result = (arm_uc_error_t){ SRCE_ERR_NONE }; 00439 arm_uc_socket_isr(NULL); 00440 } 00441 else if (pal_inner == PAL_ERR_SOCKET_IN_PROGRES ) /* asynchronous finish */ 00442 { 00443 context->expected_event = SOCKET_EVENT_CONNECT_DONE; 00444 result = (arm_uc_error_t){ SRCE_ERR_NONE }; 00445 } 00446 else 00447 { 00448 UC_SRCE_ERR_MSG("Error: socket connection failed"); 00449 result = (arm_uc_error_t){ SRCE_ERR_FAILED }; 00450 arm_uc_socket_close(); 00451 } 00452 } 00453 } 00454 00455 UC_SRCE_TRACE("arm_uc_socket_finish_connect returning %s", ARM_UC_err2Str(result)); 00456 00457 return result; 00458 } 00459 00460 00461 /** 00462 * @brief Send request passed in arm_uc_socket_get. 00463 * @details This call assumes the HTTP socket is already connected to server. 00464 * @return Error code. 00465 */ 00466 arm_uc_error_t arm_uc_socket_send_request() 00467 { 00468 /* default return value */ 00469 arm_uc_error_t result = (arm_uc_error_t){ SRCE_ERR_FAILED }; 00470 00471 /* NULL pointer check */ 00472 if (context) 00473 { 00474 /* get local references */ 00475 arm_uc_buffer_t* request_buffer = context->request_buffer; 00476 arm_uc_uri_t* request_uri = context->request_uri; 00477 arm_uc_rqst_t request_type = context->request_type; 00478 00479 /* template for generating HTTP requests */ 00480 static const char HTTP_HEADER_TEMPLATE[] = 00481 "%s %s HTTP/1.1\r\n" // status line 00482 "Host: %s\r\n"; // mandated for http 1.1 00483 00484 if (request_type == RQST_TYPE_HASH_ETAG || 00485 request_type == RQST_TYPE_HASH_DATE) 00486 { 00487 /* construct ETag and Date request header */ 00488 request_buffer->size = snprintf((char *) request_buffer->ptr, 00489 request_buffer->size_max, 00490 HTTP_HEADER_TEMPLATE, 00491 "HEAD", 00492 request_uri->path, 00493 request_uri->host); 00494 } 00495 else 00496 { 00497 /* construct download header */ 00498 request_buffer->size = snprintf((char *) request_buffer->ptr, 00499 request_buffer->size_max, 00500 HTTP_HEADER_TEMPLATE, 00501 "GET", 00502 request_uri->path, 00503 request_uri->host); 00504 } 00505 00506 if (request_type == RQST_TYPE_GET_FRAG) 00507 { 00508 /* construct the Range field that makes this a partial content request */ 00509 request_buffer->size += snprintf((char *) request_buffer->ptr + request_buffer->size, 00510 request_buffer->size_max - request_buffer->size, 00511 "Range: bytes=%" PRIu32 "-%" PRIu32 "\r\n", 00512 context->request_offset, 00513 context->request_offset + request_buffer->size_max - 1); 00514 } 00515 00516 /* terminate request with a carriage return and newline */ 00517 request_buffer->size += snprintf((char *) request_buffer->ptr + request_buffer->size, 00518 request_buffer->size_max - request_buffer->size, 00519 "\r\n"); 00520 00521 /* terminate string */ 00522 request_buffer->ptr[request_buffer->size] = '\0'; 00523 UC_SRCE_TRACE("%s", request_buffer->ptr); 00524 00525 /*************************************************************************/ 00526 00527 size_t bytes_sent = 0; 00528 00529 /* send HTTP request */ 00530 palStatus_t pal_result = pal_send(context->socket, 00531 request_buffer->ptr, 00532 request_buffer->size, 00533 &bytes_sent); 00534 00535 if (pal_result == PAL_SUCCESS) /* asynchronous finish */ 00536 { 00537 UC_SRCE_TRACE("send success"); 00538 00539 /* reset buffer and prepare to receive header */ 00540 request_buffer->size = 0; 00541 context->socket_state = STATE_PROCESS_HEADER; 00542 context->expected_event = SOCKET_EVENT_SEND_DONE; 00543 00544 result = (arm_uc_error_t){ SRCE_ERR_NONE }; 00545 } 00546 else if(pal_result == PAL_ERR_SOCKET_WOULD_BLOCK ) 00547 { 00548 UC_SRCE_TRACE("send would block, will retry"); 00549 00550 /* keep current state and force callback to retry sending */ 00551 request_buffer->size = 0; 00552 arm_uc_socket_isr(NULL); 00553 00554 result = (arm_uc_error_t){ SRCE_ERR_NONE }; 00555 } 00556 else 00557 { 00558 UC_SRCE_TRACE("send error 0x%" PRIX32, (uint32_t) pal_result); 00559 00560 /* clean up */ 00561 arm_uc_socket_error(UCS_HTTP_EVENT_ERROR); 00562 } 00563 } 00564 00565 return result; 00566 } 00567 00568 /** 00569 * @brief Receive data from HTTP socket. 00570 * @details Data is stored in global buffer. The call will automatically retry 00571 * if the socket is busy. 00572 */ 00573 void arm_uc_socket_receive() 00574 { 00575 /* NULL pointer check */ 00576 if (context) 00577 { 00578 /* get local references */ 00579 arm_uc_buffer_t* request_buffer = context->request_buffer; 00580 00581 size_t received_bytes = 0; 00582 palStatus_t pal_result = PAL_SUCCESS; 00583 00584 while ( (context->socket_state != STATE_DISCONNECTED) && 00585 (pal_result == PAL_SUCCESS) && 00586 context->request_buffer ) 00587 { 00588 if (request_buffer->size >= request_buffer->size_max) 00589 { 00590 UC_SRCE_ERR_MSG("There is no space in the buffer left"); 00591 arm_uc_socket_error(UCS_HTTP_EVENT_ERROR_BUFFER_SIZE); 00592 break; 00593 } 00594 00595 /* append data from socket receive buffer to request buffer. */ 00596 pal_result = pal_recv(context->socket, 00597 &(request_buffer->ptr[request_buffer->size]), 00598 request_buffer->size_max - request_buffer->size, 00599 &received_bytes); 00600 00601 if (pal_result == PAL_SUCCESS && received_bytes > 0) 00602 { 00603 /* Note: the proper formatter %zu is not supported on mbed's libc, 00604 * hence the casts to difference type. 00605 */ 00606 UC_SRCE_TRACE("recv success: %lu bytes received", 00607 (unsigned long)received_bytes); 00608 00609 if (request_buffer->size + received_bytes > request_buffer->size_max) 00610 { 00611 UC_SRCE_ERR_MSG("Got more data than available space in the buffer"); 00612 arm_uc_socket_error(UCS_HTTP_EVENT_ERROR_BUFFER_SIZE); 00613 break; 00614 } 00615 00616 /* update buffer size with received bytes */ 00617 request_buffer->size += received_bytes; 00618 00619 /* update expected event to signal receive done */ 00620 context->expected_event = SOCKET_EVENT_RECEIVE_CONTINUE; 00621 00622 /* received data */ 00623 if (context->socket_state == STATE_PROCESS_HEADER) 00624 { 00625 /* expecting HTTP header */ 00626 arm_uc_socket_process_header(); 00627 } 00628 else if (context->socket_state == STATE_PROCESS_BODY) 00629 { 00630 /* expecting body */ 00631 arm_uc_socket_process_body(); 00632 } 00633 else 00634 { 00635 /* unexpected data, generate error and clean up */ 00636 arm_uc_socket_error(UCS_HTTP_EVENT_ERROR); 00637 } 00638 } 00639 else if (pal_result == PAL_ERR_SOCKET_WOULD_BLOCK ) 00640 { 00641 /* Note: at least on mbed os the pal_recv() returns garbage on recievedDataSize 00642 * if the socket call returns anything but PAL_SUCCESS, so this needs to 00643 * recalculate the remaining bytes count. 00644 */ 00645 UC_SRCE_TRACE("recv: pending: %" PRIu32, 00646 (request_buffer->size_max - request_buffer->size)); 00647 00648 /* update expected event to retry receiving */ 00649 context->expected_event = SOCKET_EVENT_RECEIVE_CONTINUE; 00650 } 00651 else 00652 { 00653 UC_SRCE_ERR_MSG("Error: socket receive failed"); 00654 00655 /* clean up */ 00656 arm_uc_socket_error(UCS_HTTP_EVENT_ERROR); 00657 } 00658 } 00659 } 00660 } 00661 00662 /** 00663 * @brief Function is called when some data has been received but an HTTP 00664 * header has yet to be processed. 00665 * @details Function is called repeatedly until a header is found or the buffer 00666 * is full. Once a header is found, the ETag, date, or content length 00667 * is parsed. For file and fragment downloads the receive process is 00668 * restarted and the header is erased. 00669 */ 00670 void arm_uc_socket_process_header() 00671 { 00672 /* NULL pointer check */ 00673 if (context) 00674 { 00675 /* get local references */ 00676 arm_uc_buffer_t* request_buffer = context->request_buffer; 00677 arm_uc_uri_t* request_uri = context->request_uri; 00678 arm_uc_rqst_t request_type = context->request_type; 00679 uint32_t request_offset = context->request_offset; 00680 00681 /* setup default return to be failure */ 00682 bool request_successfully_processed = false; 00683 00684 uint32_t index = arm_uc_strnstrn(request_buffer->ptr, 00685 request_buffer->size, 00686 (const uint8_t*) "\r\n\r\n", 00687 4); 00688 00689 /* Continue receiving if full header is not found. */ 00690 if (index > request_buffer->size) 00691 { 00692 request_successfully_processed = true; 00693 arm_uc_socket_receive(); 00694 } 00695 else 00696 { 00697 /* process header */ 00698 UC_SRCE_TRACE("HTTP header found"); 00699 00700 const char header_tag[] = "HTTP/1.1 "; 00701 00702 uint32_t header_start = arm_uc_strnstrn(request_buffer->ptr, 00703 request_buffer->size, 00704 (const uint8_t*) header_tag, 00705 sizeof(header_tag) - 1); 00706 00707 /* found beginning of header */ 00708 if (header_start < request_buffer->size) 00709 { 00710 /* status code is after the header tag */ 00711 header_start = header_start + sizeof(header_tag) - 1; 00712 } 00713 00714 /* buffer size check */ 00715 if (header_start < request_buffer->size) 00716 { 00717 /* parse status code */ 00718 bool header_parsed = false; 00719 uint32_t status_code = arm_uc_str2uint32( 00720 &(request_buffer->ptr[header_start]), 00721 request_buffer->size - header_start, 00722 &header_parsed); 00723 00724 if (header_parsed == true) 00725 { 00726 UC_SRCE_TRACE("HTTP status code %" PRIu32, status_code); 00727 00728 /* Redirect status codes: 00729 301: Moved Permanently 00730 302: Found [Elsewhere] 00731 303: See Other 00732 307: Temporary Redirect 00733 */ 00734 if ((status_code >= 301 && status_code <= 303) || 00735 (status_code == 307)) 00736 { 00737 /* move location to front of buffer */ 00738 const char tag[] = "Location"; 00739 bool found = arm_uc_socket_trim_value(request_buffer, 00740 tag, 00741 sizeof(tag) - 1); 00742 00743 if (found) 00744 { 00745 /* NULL terminate string */ 00746 request_buffer->ptr[request_buffer->size] = '\0'; 00747 00748 /* parse location and store in URI */ 00749 arm_uc_error_t err = arm_uc_str2uri(request_buffer->ptr, 00750 request_buffer->size, 00751 request_uri); 00752 00753 if ((err.error == ERR_NONE) && 00754 (request_uri->scheme == URI_SCHEME_HTTP)) 00755 { 00756 UC_SRCE_TRACE("HTTP redirecting to http://%s:%" PRIu16 "/%s", 00757 request_uri->host, 00758 request_uri->port, 00759 request_uri->path); 00760 00761 /* close current socket */ 00762 arm_uc_socket_close(); 00763 00764 /* run "get" again with the new location (above) */ 00765 err = arm_uc_socket_get(request_uri, request_buffer, 00766 request_offset, request_type); 00767 if (err.error == ERR_NONE) 00768 { 00769 request_successfully_processed = true; 00770 } 00771 else 00772 { 00773 UC_SRCE_ERR_MSG("Error: HTTP redirect failed"); 00774 } 00775 } 00776 else 00777 { 00778 UC_SRCE_ERR_MSG("Error: unable to parse URI string"); 00779 } 00780 } 00781 else 00782 { 00783 UC_SRCE_ERR_MSG("Error: unable to find redirect location"); 00784 } 00785 } 00786 /* All remaining codes outside 200-226 are treated as errors */ 00787 else if (status_code < 200 || status_code > 226) 00788 { 00789 UC_SRCE_ERR_MSG("Error: server returned HTTP status code %" PRIu32, 00790 status_code); 00791 } 00792 /* All codes between 200 to 226 */ 00793 else 00794 { 00795 /* NOTE: HTTP 1.1 Code 206 with Header "Connection:close" is not 00796 handled here, instead the execution falls trough to error- 00797 handling in http_socket (ARM_UCS_HTTPEVent with UCS_HTTP_EVENT_ERROR) 00798 where the retry-mechanism will resume firmware download if 00799 the server closed the connection. 00800 */ 00801 if (request_type == RQST_TYPE_HASH_ETAG) 00802 { 00803 /* look for ETag and move to front of buffer */ 00804 const char tag[] = "ETag"; 00805 bool found = arm_uc_socket_trim_value(request_buffer, 00806 tag, 00807 sizeof(tag) - 1); 00808 00809 if (found) 00810 { 00811 /* ETag successfully read - post callback */ 00812 if (context->callback_handler) 00813 { 00814 palStatus_t status = pal_osTimerStop(context->timeout_timer_id); 00815 if (status != PAL_SUCCESS) 00816 { 00817 UC_SRCE_ERR_MSG("pal_osTimerStop returned 0x%" PRIX32, 00818 (uint32_t) status); 00819 arm_uc_socket_close(); 00820 } 00821 00822 ARM_UC_PostCallback(&context->event_callback_struct, 00823 context->callback_handler, 00824 UCS_HTTP_EVENT_HASH); 00825 } 00826 00827 /* request complete - close socket */ 00828 arm_uc_socket_close(); 00829 00830 /* success - no clean up needed */ 00831 request_successfully_processed = true; 00832 } 00833 else 00834 { 00835 UC_SRCE_ERR_MSG("Error: unable to find ETag"); 00836 } 00837 } 00838 else if (request_type == RQST_TYPE_HASH_DATE) 00839 { 00840 /* look for date and move to front of buffer */ 00841 const char tag[] = "Last-Modified"; 00842 bool found = arm_uc_socket_trim_value(request_buffer, 00843 tag, 00844 sizeof(tag) - 1); 00845 00846 if (found) 00847 { 00848 /* date successfully read - post callback */ 00849 if (context->callback_handler) 00850 { 00851 palStatus_t status = pal_osTimerStop(context->timeout_timer_id); 00852 if (status != PAL_SUCCESS) 00853 { 00854 UC_SRCE_ERR_MSG("pal_osTimerStop returned 0x%" PRIX32, 00855 (uint32_t) status); 00856 arm_uc_socket_close(); 00857 } 00858 00859 ARM_UC_PostCallback(&context->event_callback_struct, 00860 context->callback_handler, 00861 UCS_HTTP_EVENT_DATE); 00862 } 00863 00864 /* request complete - close socket */ 00865 arm_uc_socket_close(); 00866 00867 /* signal clean up is not needed */ 00868 request_successfully_processed = true; 00869 } 00870 else 00871 { 00872 UC_SRCE_ERR_MSG("Error: unable to find last modified date"); 00873 } 00874 } 00875 /* request is GetFile or GetFragment */ 00876 else 00877 { 00878 /* save current buffer size so we can recover body after 00879 the content length has been read. */ 00880 uint32_t current_size = request_buffer->size; 00881 00882 /* find content length and move value to front of buffer */ 00883 const char tag[] = "Content-Length"; 00884 bool found = arm_uc_socket_trim_value(request_buffer, 00885 tag, 00886 sizeof(tag) - 1); 00887 00888 if (found) 00889 { 00890 /* NULL terminate string */ 00891 request_buffer->ptr[request_buffer->size] = '\0'; 00892 00893 /* parse full length of content */ 00894 int parsed = sscanf((char *) request_buffer->ptr, 00895 "%10" SCNu32, 00896 &context->expected_remaining); 00897 00898 /* only continue if exactly one argument was parsed */ 00899 if (parsed == 1) 00900 { 00901 UC_SRCE_TRACE("content: %" PRIu32, 00902 context->expected_remaining); 00903 00904 /* replace HTTP header with body */ 00905 memmove(request_buffer->ptr, 00906 &(request_buffer->ptr[index + 4]), 00907 current_size - (index + 4)); 00908 00909 /* set size of partial body */ 00910 request_buffer->size = current_size - (index + 4); 00911 00912 /* */ 00913 if (request_buffer->size >= context->expected_remaining) 00914 { 00915 /* all data received - process data */ 00916 arm_uc_socket_process_body(); 00917 } 00918 else 00919 { 00920 /* expecting more data - continue receiving */ 00921 UC_SRCE_TRACE("expecting more data %" PRIu32 "/%" PRIu32 "\r\n", 00922 request_buffer->size, 00923 context->expected_remaining); 00924 } 00925 00926 /* signal clean up is not needed */ 00927 request_successfully_processed = true; 00928 00929 /* continue processing body */ 00930 context->socket_state = STATE_PROCESS_BODY; 00931 } 00932 else 00933 { 00934 UC_SRCE_ERR_MSG("Error: unable to parse content length"); 00935 } 00936 } 00937 else 00938 { 00939 UC_SRCE_ERR_MSG("Error: unable find content length"); 00940 } 00941 } 00942 } 00943 } 00944 else 00945 { 00946 UC_SRCE_ERR_MSG("Error: unable to read status code"); 00947 } 00948 } 00949 else 00950 { 00951 UC_SRCE_ERR_MSG("Error: HTTP header not found"); 00952 } 00953 } 00954 00955 /* clean up */ 00956 if (request_successfully_processed == false) 00957 { 00958 arm_uc_socket_error(UCS_HTTP_EVENT_ERROR); 00959 } 00960 } 00961 } 00962 00963 /** 00964 * @brief Function is called when file or fragment is being downloaded. 00965 * @details Function drives the download and continues until the buffer is full 00966 * or the expected amount of data has been downloaded. 00967 */ 00968 void arm_uc_socket_process_body() 00969 { 00970 /* NULL pointer check */ 00971 if (context) 00972 { 00973 /* check if all expected bytes have been received */ 00974 if (context->request_buffer->size >= context->expected_remaining) 00975 { 00976 UC_SRCE_TRACE("process body done"); 00977 00978 /* fragment or file successfully received - post callback */ 00979 if (context->callback_handler) 00980 { 00981 palStatus_t status = pal_osTimerStop(context->timeout_timer_id); 00982 if (status != PAL_SUCCESS) 00983 { 00984 UC_SRCE_ERR_MSG("pal_osTimerStop returned 0x%" PRIX32, 00985 (uint32_t) status); 00986 arm_uc_socket_close(); 00987 } 00988 00989 ARM_UC_PostCallback(&context->event_callback_struct, 00990 context->callback_handler, 00991 UCS_HTTP_EVENT_DOWNLOAD); 00992 } 00993 00994 /* reset buffers and state */ 00995 context->socket_state = STATE_CONNECTED_IDLE; 00996 context->request_buffer = NULL; 00997 context->expected_event = SOCKET_EVENT_UNDEFINED; 00998 } 00999 } 01000 } 01001 01002 /** 01003 * @brief Close socket and set internal state to disconnected. 01004 */ 01005 void arm_uc_socket_close() 01006 { 01007 /* NULL pointer check */ 01008 if (context) 01009 { 01010 /* close socket if not NULL */ 01011 if (context->socket) 01012 { 01013 pal_close(&context->socket); 01014 } 01015 01016 /* delete socket timeout timer */ 01017 if (context->timeout_timer_id != (palTimerID_t) NULL) 01018 { 01019 pal_osTimerDelete(&context->timeout_timer_id); 01020 } 01021 01022 /* reset buffers and state */ 01023 context->request_buffer = NULL; 01024 context->expected_event = SOCKET_EVENT_UNDEFINED; 01025 context->socket_state = STATE_DISCONNECTED; 01026 context->timeout_timer_id = 0; 01027 } 01028 } 01029 01030 /** 01031 * @brief Close socket, set internal state to disconnected and generate error 01032 * event. 01033 */ 01034 void arm_uc_socket_error(arm_ucs_http_event_t error) 01035 { 01036 /* NULL pointer check */ 01037 if (context) 01038 { 01039 if (context->socket_state != STATE_DISCONNECTED) 01040 { 01041 /* close socket */ 01042 arm_uc_socket_close(); 01043 } 01044 01045 /* clear DNS cache */ 01046 context->cache_address.addressType = 0; 01047 memset(context->cache_address.addressData, 0, PAL_NET_MAX_ADDR_SIZE); 01048 context->cache_address_length = 0; 01049 01050 /* if callback handler is set, generate error event */ 01051 if (context->callback_handler) 01052 { 01053 UC_SRCE_ERR_MSG("posting to callback with event %d", error); 01054 ARM_UC_PostCallback(&context->event_callback_struct, 01055 context->callback_handler, 01056 error); 01057 } 01058 } 01059 } 01060 01061 /** 01062 * @brief PAL socket event handler. 01063 * @param unused PAL API doesn't support parameters. 01064 */ 01065 void arm_uc_socket_callback(uint32_t unused) 01066 { 01067 (void) unused; 01068 01069 UC_SRCE_TRACE("arm_uc_socket_callback"); 01070 01071 /* NULL pointer check */ 01072 if (context) 01073 { 01074 /* unlock posting callbacks to the queue */ 01075 pal_osAtomicIncrement(&context->isr_callback_counter, -1); 01076 01077 switch (context->expected_event) 01078 { 01079 case SOCKET_EVENT_DNS_DONE: 01080 UC_SRCE_TRACE("DNS done"); 01081 01082 { 01083 /* stop DNS timeout timer */ 01084 arm_uc_error_t result = arm_uc_stop_dns_timer(); 01085 if (result.code != SRCE_ERR_NONE) 01086 { 01087 arm_uc_socket_error(UCS_HTTP_EVENT_ERROR); 01088 } 01089 else 01090 { 01091 if (context->cache_address.addressType == 0 && 01092 context->cache_address_length == 0) 01093 { 01094 UC_SRCE_ERR_MSG("DNS resolution failed for host %s", 01095 context->request_uri->host); 01096 arm_uc_socket_error(UCS_HTTP_EVENT_ERROR); 01097 } 01098 else 01099 { 01100 result = arm_uc_socket_finish_connect(); 01101 if (result.code != SRCE_ERR_NONE) 01102 { 01103 UC_SRCE_ERR_MSG("arm_uc_socket_finish_connect failed: %s", 01104 ARM_UC_err2Str(result)); 01105 arm_uc_socket_error(UCS_HTTP_EVENT_ERROR); 01106 } 01107 } 01108 } 01109 } 01110 break; 01111 01112 case SOCKET_EVENT_CONNECT_DONE: 01113 UC_SRCE_TRACE("Connect done"); 01114 01115 context->socket_state = STATE_CONNECTED_IDLE; 01116 { 01117 arm_uc_error_t result = arm_uc_socket_send_request(); 01118 if (result.code != SRCE_ERR_NONE) 01119 { 01120 UC_SRCE_ERR_MSG("arm_uc_socket_send_request failed: %s", 01121 ARM_UC_err2Str(result)); 01122 arm_uc_socket_error(UCS_HTTP_EVENT_ERROR); 01123 } 01124 } 01125 break; 01126 01127 case SOCKET_EVENT_SEND_DONE: 01128 UC_SRCE_TRACE("send done"); 01129 01130 /* request send, receive response */ 01131 context->expected_event = SOCKET_EVENT_RECEIVE_CONTINUE; 01132 arm_uc_socket_receive(); 01133 break; 01134 01135 case SOCKET_EVENT_RECEIVE_CONTINUE: 01136 UC_SRCE_TRACE("recv continue"); 01137 01138 /* outstanding data, continue receiving */ 01139 arm_uc_socket_receive(); 01140 break; 01141 01142 case SOCKET_EVENT_TIMER_FIRED: 01143 UC_SRCE_TRACE("socket timeout timer fired"); 01144 01145 /* delete socket timeout timer */ 01146 if (context->timeout_timer_id != (palTimerID_t) NULL) 01147 { 01148 pal_osTimerDelete(&context->timeout_timer_id); 01149 context->timeout_timer_id = 0; 01150 } 01151 arm_uc_socket_error(UCS_HTTP_EVENT_ERROR); 01152 break; 01153 01154 case SOCKET_EVENT_UNDEFINED: 01155 default: 01156 UC_SRCE_TRACE("event: undefined"); 01157 break; 01158 } 01159 } 01160 } 01161 01162 /** 01163 * @brief Callback handler for PAL socket events. Callbacks go through the task 01164 * queue because we don't know what context we are running from. 01165 */ 01166 void arm_uc_socket_isr(void* unused) 01167 { 01168 /* NULL pointer check */ 01169 if (context) 01170 { 01171 /* ensure we only have one callback in flight */ 01172 int32_t count = pal_osAtomicIncrement(&context->isr_callback_counter, 0); 01173 01174 if (count == 0) 01175 { 01176 pal_osAtomicIncrement(&context->isr_callback_counter, 1); 01177 01178 /* post callback to de-escalate event */ 01179 ARM_UC_PostCallback(&context->isr_callback_struct, 01180 arm_uc_socket_callback, 01181 0); 01182 } 01183 } 01184 } 01185 01186 /** 01187 * @brief Callback handler for the socket timeout timer callback. 01188 * Callbacks go through the task queue because we don't know 01189 * what context we are running from. 01190 */ 01191 void arm_uc_timeout_timer_callback(void const *unused) 01192 { 01193 (void) unused; 01194 01195 if (context != NULL) 01196 { 01197 context->expected_event = SOCKET_EVENT_TIMER_FIRED; 01198 /* push event to the socket event queue */ 01199 arm_uc_socket_isr(NULL); 01200 } 01201 } 01202 01203 /** 01204 * @brief Callback handler for the asynchronous DNS resolver. 01205 * Callbacks go through the task queue because we don't know 01206 * what context we are running from. 01207 */ 01208 void arm_uc_dns_callback(const char* url, palSocketAddress_t* address, 01209 palSocketLength_t* address_length, palStatus_t status, 01210 void *argument) 01211 { 01212 (void)url; 01213 (void)address; 01214 (void)address_length; 01215 (void)argument; 01216 01217 if (context != NULL) 01218 { 01219 /* check if DNS call succeeded */ 01220 if (status != PAL_SUCCESS) 01221 { 01222 /* clear the address-related fields to signal an error */ 01223 context->cache_address.addressType = 0; 01224 context->cache_address_length = 0; 01225 } 01226 context->expected_event = SOCKET_EVENT_DNS_DONE; 01227 /* push event to the socket event queue */ 01228 arm_uc_socket_isr(NULL); 01229 } 01230 }
Generated on Tue Jul 12 2022 19:12:11 by 1.7.2