Simulated product dispenser

Dependencies:   HTS221

Fork of mbed-cloud-workshop-connect-HTS221 by Jim Carver

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  * 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 }