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