Simple interface for Mbed Cloud Client

Dependents:  

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers arm_uc_http_socket_private.c Source File

arm_uc_http_socket_private.c

00001 // ----------------------------------------------------------------------------
00002 // Copyright 2016-2017 ARM Ltd.
00003 //
00004 // SPDX-License-Identifier: Apache-2.0
00005 //
00006 // Licensed under the Apache License, Version 2.0 (the "License");
00007 // you may not use this file except in compliance with the License.
00008 // You may obtain a copy of the License at
00009 //
00010 //     http://www.apache.org/licenses/LICENSE-2.0
00011 //
00012 // Unless required by applicable law or agreed to in writing, software
00013 // distributed under the License is distributed on an "AS IS" BASIS,
00014 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015 // See the License for the specific language governing permissions and
00016 // limitations under the License.
00017 // ----------------------------------------------------------------------------
00018 
00019 #include "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 }