diff options
-rw-r--r-- | inc/cloud/car_info.h | 6 | ||||
-rw-r--r-- | inc/cloud/cloud_request.h | 47 | ||||
-rw-r--r-- | src/cloud/car_info.c | 20 | ||||
-rw-r--r-- | src/cloud/car_info_serializer.c | 19 | ||||
-rw-r--r-- | src/cloud/cloud_request.c | 245 |
5 files changed, 319 insertions, 18 deletions
diff --git a/inc/cloud/car_info.h b/inc/cloud/car_info.h index 2daeb0c..da95734 100644 --- a/inc/cloud/car_info.h +++ b/inc/cloud/car_info.h @@ -31,6 +31,12 @@ typedef struct car_info car_info_t; car_info_t *car_info_create(); /** + * @brief Crates deep copy of given car_info_t object. + * @return Copied car_info_t object, or NULL on error. + */ +car_info_t *car_info_copy(const car_info_t *car_info); + +/** * @brief Releases car_info_t. * @param[in] car_info Car info struct. */ diff --git a/inc/cloud/cloud_request.h b/inc/cloud/cloud_request.h index f266d04..8137fed 100644 --- a/inc/cloud/cloud_request.h +++ b/inc/cloud/cloud_request.h @@ -17,33 +17,56 @@ #ifndef __CLOUD_REQUEST_H_ #define __CLOUD_REQUEST_H_ +#include <gio/gio.h> #include "car_info.h" /** - * @brief Called when data from HTTP /api/racing request was obtained. + * @brief Enum that indicates if HTTP request finished succesfully or not. +*/ +typedef enum +{ + SUCCESS = 0, + FAILURE +} request_result_e; + +/** + * @brief Called when data from HTTP /api/racing GET request was obtained. + * + * @param[in] result Result of request. * @param[in] car_info The array of car_info struct. * @param[in] size Length of the car_info array + * @param[in] user_data User data passed in @cloud_request_api_racing_get function. + */ +typedef void(*cloud_request_car_list_data_cb)(request_result_e result, car_info_t **car_info, int size, void *user_data); + +/** + * @brief Called when data from HTTP /api/racing POST request was obtained. + * + * @param[in] result Result of request. + * @param[in] user_data User data passed in @cloud_request_api_racing_post function. */ -typedef void(*cloud_request_car_info_data_cb)(car_info_t *car_info, int size); +typedef void(*cloud_request_car_post_finish_cb)(request_result_e result, void *user_data); /** * @brief Sends cloud request that obtains list of registered cars. + * * @param[in] ap_mac Mac address of access point that device is connected to. - * @return Returns 0 on success, -1 otherwise. + * @param[in] callback Function that will be invoked, when request will be finished. + * + * @return Returns @GCancellable object that allows to cancel this request. + * @remark To cancel task function g_cancellable_cancel should be called. */ -int cloud_request_api_racing_get(const char *ap_mac); +GCancellable *cloud_request_api_racing_get(const char *ap_mac, cloud_request_car_list_data_cb callback, void *user_data); /** * @brief Sends cloud request registering the car. + * * @param[in] car_info The car_info object with car data. - * @return Returns 0 on success, -1 otherwise. - */ -int cloud_request_api_racing_post(car_info_t *car_info); - -/** - * @brief Sets callback that will be called when data from request will be obtained. - * @param[in] callback Callback function. + * @param[in] callback Function that will be invoked, when request will be finished. + * + * @return Returns @GCancellable object that allows to cancel this request. + * @remark To cancel task function g_cancellable_cancel should be called. */ -void cloud_request_data_cb_set(cloud_request_car_info_data_cb callback); +GCancellable *cloud_request_api_racing_post(const car_info_t *car_info, cloud_request_car_post_finish_cb callback, void *user_data); #endif
\ No newline at end of file diff --git a/src/cloud/car_info.c b/src/cloud/car_info.c index f54d31a..7c977d7 100644 --- a/src/cloud/car_info.c +++ b/src/cloud/car_info.c @@ -29,6 +29,12 @@ #define MAX_LENGTH_MAC 18 #define MAX_LENGTH_SSID 33 +#define SAFE_STR_CPY(src, obj, size)\ +do {\ + if(obj)\ + src = strndup(obj, size);\ +} while(0) + static int validate_ip_address(const char *ip_address); static int validate_mac_address(const char *mac_address); @@ -49,6 +55,20 @@ car_info_t *car_info_create() return car_info; } +car_info_t *car_info_copy(const car_info_t *car_info) +{ + retv_if(!car_info, NULL); + + struct car_info *car_info_cpy = g_new0(struct car_info, 1); + SAFE_STR_CPY(car_info_cpy->id, car_info->id, MAX_LENGTH); + SAFE_STR_CPY(car_info_cpy->name, car_info->name, MAX_LENGTH); + SAFE_STR_CPY(car_info_cpy->ip, car_info->ip, MAX_LENGTH_IP); + SAFE_STR_CPY(car_info_cpy->ap_mac, car_info->ap_mac, MAX_LENGTH_MAC); + SAFE_STR_CPY(car_info_cpy->ap_ssid, car_info->ap_ssid, MAX_LENGTH_SSID); + + return car_info_cpy; +} + void car_info_destroy(car_info_t *car_info) { ret_if(!car_info); diff --git a/src/cloud/car_info_serializer.c b/src/cloud/car_info_serializer.c index a9bf9f3..3ee74f6 100644 --- a/src/cloud/car_info_serializer.c +++ b/src/cloud/car_info_serializer.c @@ -21,8 +21,10 @@ #include <json-glib/json-glib.h> #include "log.h" #include "cloud/car_info.h" +#include "string.h" #define JSON_SCHEMA_CAR_ID "carId" +#define JSON_SCHEMA_RESPONSE_CAR_ID "id" #define JSON_SCHEMA_CAR_NAME "carName" #define JSON_SCHEMA_CAR_IP "carIp" #define JSON_SCHEMA_AP_MAC "apMac" @@ -72,12 +74,17 @@ car_info_t **car_info_serializer_deserialize_array(const char *json_data, int *s JsonArray *array = json_node_get_array(root); if (!array) { - _E("Json config is invalid!"); + _E("Json is invalid!"); g_object_unref(parser); return NULL; } *size = json_array_get_length(array); + + if (*size == 0) { + return NULL; + } + car_info_t **car_info_array = g_malloc(*size * sizeof(car_info_t *)); json_array_foreach_element(array, car_info_array_iterate_cb, car_info_array); @@ -86,13 +93,13 @@ car_info_t **car_info_serializer_deserialize_array(const char *json_data, int *s return car_info_array; } -static JsonNode *parse_string(JsonParser *parser, const char *config_json) +static JsonNode *parse_string(JsonParser *parser, const char *json) { GError *err = NULL; - if (!json_parser_load_from_data(parser, config_json, -1, &err)) + if (!json_parser_load_from_data(parser, json, -1, &err)) { - _E("Function \"json_parser_load_from_data()\" failed with message: ", err->message); + _E("Function \"json_parser_load_from_data()\" failed with message: %s", err->message); g_error_free(err); return NULL; } @@ -108,8 +115,8 @@ static void car_info_array_iterate_cb(JsonArray *array, guint index, JsonNode *e car_info_array[index] = car_info_create(); - if (json_object_has_member(entry, JSON_SCHEMA_CAR_ID) && - car_info_set_car_id(car_info_array[index], json_object_get_string_member(entry, JSON_SCHEMA_CAR_ID)) != 0) + if (json_object_has_member(entry, JSON_SCHEMA_RESPONSE_CAR_ID) && + car_info_set_car_id(car_info_array[index], json_object_get_string_member(entry, JSON_SCHEMA_RESPONSE_CAR_ID)) != 0) { _E("Couldn't set car id!"); } diff --git a/src/cloud/cloud_request.c b/src/cloud/cloud_request.c new file mode 100644 index 0000000..1a46fa1 --- /dev/null +++ b/src/cloud/cloud_request.c @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * + * Licensed under the Flora License, Version 1.1 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cloud/cloud_request.h" +#include "cloud/car_info_serializer.h" +#include "cloud/http_request.h" +#include <glib.h> +#include <gio/gio.h> +#include <string.h> +#include "log.h" + +#define BASE_URL "https://son.tizen.online" +#define PATH_API_RACING "/api/racing" + +typedef struct { + char *ap_mac; + cloud_request_car_list_data_cb cb; + void *user_data; +} car_api_get_request_context_t; + +typedef struct { + car_info_t *car; + cloud_request_car_post_finish_cb cb; + void *user_data; +} car_api_post_request_context_t; + +typedef struct { + long response_code; + char *response_msg; +} car_api_post_request_response_t; + +typedef struct { + long response_code; + car_info_t **cars; + int size; +} car_api_get_request_response_t; + +GQuark g_spawn_error_quark(); +static void car_api_post_task_thread_cb(GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable); +static void car_api_post_task_ready_cb(GObject *source_object, GAsyncResult *res, gpointer user_data); +static void car_api_post_task_context_free(car_api_post_request_context_t *context); +static void car_api_post_request_response_free(car_api_post_request_response_t *response); + +static void car_api_get_task_context_free(car_api_get_request_context_t *context); +static void car_api_get_request_response_free(car_api_get_request_response_t *response); +static void car_api_get_task_thread_cb(GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable); +static void car_api_get_task_ready_cb(GObject *source_object, GAsyncResult *res, gpointer user_data); + +#define G_ERROR_DOMAIN g_spawn_error_quark() + +GCancellable *cloud_request_api_racing_get(const char *ap_mac, cloud_request_car_list_data_cb cb, void *user_data) +{ + GCancellable *cancellable = g_cancellable_new(); + + GTask *task = g_task_new(NULL, cancellable, car_api_get_task_ready_cb, NULL); + g_task_set_source_tag(task, cloud_request_api_racing_get); + g_task_set_return_on_cancel(task, FALSE); + + car_api_get_request_context_t *context = g_new0(car_api_get_request_context_t, 1); + context->ap_mac = strndup(ap_mac, strlen(ap_mac)); + context->cb = cb; + context->user_data = user_data; + + g_task_set_task_data(task, context, (GDestroyNotify)car_api_get_task_context_free); + g_task_run_in_thread(task, car_api_get_task_thread_cb); + + g_object_unref(task); + + return cancellable; +} + +GCancellable *cloud_request_api_racing_post(const car_info_t *car_info, cloud_request_car_post_finish_cb cb, void *user_data) +{ + GCancellable *cancellable = g_cancellable_new(); + + GTask *task = g_task_new(NULL, cancellable, car_api_post_task_ready_cb, NULL); + g_task_set_source_tag(task, cloud_request_api_racing_post); + g_task_set_return_on_cancel(task, FALSE); + + car_api_post_request_context_t *context = g_new0(car_api_post_request_context_t, 1); + context->car = car_info_copy(car_info); + context->cb = cb; + context->user_data = user_data; + + g_task_set_task_data(task, context, (GDestroyNotify)car_api_post_task_context_free); + g_task_run_in_thread(task, car_api_post_task_thread_cb); + + g_object_unref(task); + + return cancellable; +} + +GQuark g_spawn_error_quark() +{ + return g_quark_from_static_string("cloud_request"); +} + +static void car_api_post_task_context_free(car_api_post_request_context_t *context) +{ + ret_if(!context); + + car_info_destroy(context->car); + g_free(context); +} + +static void car_api_post_request_response_free(car_api_post_request_response_t *response) +{ + ret_if(!response); + + g_free(response->response_msg); + g_free(response); +} + +static void car_api_post_task_thread_cb(GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) +{ + car_api_post_request_context_t *context = (car_api_post_request_context_t *)task_data; + + if (g_task_return_error_if_cancelled(task)) { + return; + } + + car_api_post_request_response_t *response = g_new0(car_api_post_request_response_t, 1); + + char *json = car_info_serializer_serialize(context->car); + int retval = http_request_post(BASE_URL""PATH_API_RACING, json, &(response->response_msg), &(response->response_code)); + g_free(json); + + if (retval != 0) { + GError *err = g_error_new(G_ERROR_DOMAIN, retval, "http_request_post failed!"); + g_task_return_error(task, err); + } + + g_task_return_pointer(task, response, (GDestroyNotify)car_api_post_request_response_free); +} + +static void car_api_post_task_ready_cb(GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + GTask *task = G_TASK(res); + GError *error = NULL; + + //If no error occurred g_task_propagate_pointer transfers ownership, so later response have to be freed. + car_api_post_request_response_t *response = g_task_propagate_pointer(task, &error); + if (error != NULL) { + _E("POST async task failed with msg: %s", error->message); + g_error_free(error); + return; + } + car_api_post_request_context_t *context = g_task_get_task_data(task); + + request_result_e result = (response->response_code == 200 && (strcmp(response->response_msg, "Success") == 0)) ? + SUCCESS : + FAILURE; + + if (context->cb) { + context->cb(result, context->user_data); + } + + car_api_post_request_response_free(response); +} + +static void car_api_get_task_context_free(car_api_get_request_context_t *context) +{ + ret_if(!context); + + g_free(context->ap_mac); + g_free(context); +} + +static void car_api_get_request_response_free(car_api_get_request_response_t *response) +{ + ret_if(!response); + ret_if(response->size <= 0); + + for (int i = 0; i < response->size; i++) + { + car_info_destroy(response->cars[i]); + } + g_free(response->cars); + g_free(response); +} + +static void car_api_get_task_thread_cb(GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) +{ + car_api_get_request_context_t *context = (car_api_get_request_context_t *)task_data; + + if (g_task_return_error_if_cancelled(task)) { + return; + } + + car_api_get_request_response_t *response = g_new0(car_api_get_request_response_t, 1); + char *response_json = NULL; + + GString *url = g_string_new(BASE_URL""PATH_API_RACING"?apMac="); + g_string_append(url, context->ap_mac); + + int retval = http_request_get(url->str, &response_json, &(response->response_code)); + g_string_free(url, TRUE); + + if (retval != 0) { + GError *err = g_error_new(G_ERROR_DOMAIN, retval, "http_request_get failed!"); + g_task_return_error(task, err); + } + else { + response->cars = car_info_serializer_deserialize_array(response_json, &(response->size)); + } + + g_free(response_json); + g_task_return_pointer(task, response, (GDestroyNotify)car_api_get_request_response_free); +} + +static void car_api_get_task_ready_cb(GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + GTask *task = G_TASK(res); + GError *error = NULL; + + //If no error occurred g_task_propagate_pointer transfers ownership, so later response have to be freed. + car_api_get_request_response_t *response = g_task_propagate_pointer(task, &error); + if (error != NULL) { + _E("GET async task failed with msg: %s", error->message); + g_error_free(error); + return; + } + car_api_get_request_context_t *context = g_task_get_task_data(task); + + request_result_e result = (response->response_code == 200) ? SUCCESS : FAILURE; + + if (context->cb) { + context->cb(result, response->cars, response->size, context->user_data); + } + + car_api_get_request_response_free(response); +}
\ No newline at end of file |