diff options
author | Shibata Makoto <shibata@mac.tec.toyota.co.jp> | 2013-07-17 19:44:53 +0900 |
---|---|---|
committer | Shibata Makoto <shibata@mac.tec.toyota.co.jp> | 2013-07-17 19:47:16 +0900 |
commit | de598a444a2dba333c90eca39ea6734a09be3300 (patch) | |
tree | b06442c253f8d9d1fe88396c7438843c1e80ca4d | |
download | ico-uxf-utilities-de598a444a2dba333c90eca39ea6734a09be3300.tar.gz ico-uxf-utilities-de598a444a2dba333c90eca39ea6734a09be3300.tar.bz2 ico-uxf-utilities-de598a444a2dba333c90eca39ea6734a09be3300.zip |
Import initial.
Change-Id: I4f2d6a1747e069582626f68346ae97744a5584c4
Signed-off-by: Shibata Makoto <shibata@mac.tec.toyota.co.jp>
-rw-r--r-- | Makefile.am | 4 | ||||
-rwxr-xr-x | autogen.sh | 9 | ||||
-rw-r--r-- | configure.ac | 45 | ||||
-rw-r--r-- | include/ico_uws.h | 198 | ||||
-rw-r--r-- | include/ico_uws_private.h | 180 | ||||
-rw-r--r-- | packaging/ico-uxf-utilities.changes | 2 | ||||
-rw-r--r-- | packaging/ico-uxf-utilities.spec | 58 | ||||
-rw-r--r-- | src/Makefile.am | 17 | ||||
-rw-r--r-- | src/ico_uws.c | 1410 | ||||
-rw-r--r-- | test/Makefile.am | 37 | ||||
-rwxr-xr-x | test/run_test.sh | 331 | ||||
-rw-r--r-- | test/tst_ico_uws.h | 41 | ||||
-rw-r--r-- | test/tst_ico_uws_client.c | 368 | ||||
-rw-r--r-- | test/tst_ico_uws_multi_client.c | 548 | ||||
-rw-r--r-- | test/tst_ico_uws_multi_server.c | 541 | ||||
-rw-r--r-- | test/tst_ico_uws_server.c | 328 |
16 files changed, 4117 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..0b75466 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS = src test + +DIST_SUBDIRS = src test + diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..916169a --- /dev/null +++ b/autogen.sh @@ -0,0 +1,9 @@ +#! /bin/sh + +test -n "$srcdir" || srcdir=`dirname "$0"` +test -n "$srcdir" || srcdir=. +( + cd "$srcdir" && + autoreconf --force -v --install +) || exit +test -n "$NOCONFIGURE" || "$srcdir/configure" "$@" diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..40e85fd --- /dev/null +++ b/configure.ac @@ -0,0 +1,45 @@ +AC_PREREQ([2.68]) +AC_INIT([ico-uxf-utilities], + [0.2.01], + [https://BUG-REPORT-ADDRESS]) + +AC_CONFIG_HEADERS([config.h]) + +AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz]) + +AM_SILENT_RULES([yes]) + +# Check for programs +AC_PROG_CC + +# Initialize libtool +LT_PREREQ([2.2]) +LT_INIT([disable-static]) + +PKG_PROG_PKG_CONFIG() + +PKG_CHECK_MODULES([GLIB], [glib-2.0]) +PKG_CHECK_MODULES([DLOG], [dlog]) +OPT_CFLAGS="$GLIB_CFLAGS $DLOG_CFLAGS" +OPT_LIBS="$GLIB_LIBS $DLOG_LIBS" +AC_SUBST(OPT_CFLAGS) +AC_SUBST(OPT_LIBS) + +AC_CHECK_HEADERS([execinfo.h]) + +AC_CHECK_FUNCS([mkostemp strchrnul]) + +if test "x$GCC" = "xyes"; then + my_common_gcc_flags="-Wall -Wextra -Wno-unused-parameter \ + -Wno-missing-field-initializers -g -fvisibility=hidden" + GCC_CFLAGS="$my_common_gcc_flags \ + -Wstrict-prototypes -Wmissing-prototypes" + GCC_CXXFLAGS="$my_common_gcc_flags" +fi +AC_SUBST(GCC_CFLAGS) +AC_SUBST(GCC_CXXFLAGS) + +AC_CONFIG_FILES([Makefile + src/Makefile + test/Makefile]) +AC_OUTPUT diff --git a/include/ico_uws.h b/include/ico_uws.h new file mode 100644 index 0000000..bc53f68 --- /dev/null +++ b/include/ico_uws.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2013, TOYOTA MOTOR CORPORATION. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ +/** + * @brief header file of library for communicate + * + * @date June-7-2013 + */ + +#ifndef __ICO_UWS_H__ +#define __ICO_UWS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/*============================================================================*/ +/* definition */ +/*============================================================================*/ +struct ico_uws_context; + +typedef enum { + ICO_UWS_STATE_CONNECTING = 0, + ICO_UWS_STATE_OPEN = 1, + ICO_UWS_STATE_CLOSING = 2, + ICO_UWS_STATE_CLOSED = 3, + ICO_UWS_STATE_UNKNOWN = -1 +} ico_uws_state_e; + +typedef enum { + ICO_UWS_EVT_OPEN = 100, + ICO_UWS_EVT_ERROR = 101, + ICO_UWS_EVT_CLOSE = 102, + ICO_UWS_EVT_RECEIVE = 103, + ICO_UWS_EVT_ADD_FD = 104, + ICO_UWS_EVT_DEL_FD = 105 +} ico_uws_evt_e; + +typedef enum { + ICO_UWS_ERR_NONE = 0, /**< success */ + ICO_UWS_ERR_CREATE = -201, + ICO_UWS_ERR_CONNECT = -202, + ICO_UWS_ERR_CLOSED = -203, + ICO_UWS_ERR_SEND = -204, + ICO_UWS_ERR_INVALID_PARAM = -205, + ICO_UWS_ERR_OUT_OF_MEMORY = -206, + ICO_UWS_ERR_UNKNOWN = -300, +} ico_uws_error_e; + +typedef union { + struct { + void *recv_data; + size_t recv_len; + } _ico_uws_message; + struct { + ico_uws_error_e code; + } _ico_uws_error; + struct { + int fd; + } _ico_uws_fd; +} ico_uws_detail; + +typedef void (*ico_uws_evt_cb) + (const struct ico_uws_context *context, + const ico_uws_evt_e event, + const void *id, + const ico_uws_detail *detail, + void *user_data); + +/*============================================================================*/ +/* functions */ +/*============================================================================*/ +/*--------------------------------------------------------------------------*/ +/** + * @brief ico_uws_create_context + * Create ico_uws context. + * This API does not support secure access ("wss://") and + * the multi protocols. + * (If user sets "wss://", this function processes as "ws://".) + * + * @param[in] uri the uri to which to connect + * server sets the string ":(port)" + * client sets the string "ws://(host):(port)" + * @param[in] protocol the protocol name + * @return context + * @retval ico_uws context success + * @retval NULL error + */ +/*--------------------------------------------------------------------------*/ +struct ico_uws_context *ico_uws_create_context(const char *uri, + const char *protocol); + +/*--------------------------------------------------------------------------*/ +/** + * @brief ico_uws_close + * Close the connection and destroy the ico_uws context. + * + * @param[in] context ico_uws context + * @return none + */ +/*--------------------------------------------------------------------------*/ +void ico_uws_close(struct ico_uws_context *context); + +/*--------------------------------------------------------------------------*/ +/** + * @brief ico_uws_send + * Send data to which to connect. + * User needs to call the function "ico_uws_service" + * before calling this function. + * + * @param[in] context ico_uws context + * @param[in] id the id to connected to (callback notifies) + * @param[in] data the data to send + * @param[in] len count of the data bytes + * @return none + * @see ico_uws_service + */ +/*--------------------------------------------------------------------------*/ +void ico_uws_send(struct ico_uws_context *context, void *id, + unsigned char *data, size_t len); + +/*--------------------------------------------------------------------------*/ +/** + * @brief ico_uws_service + * Service any pending websocket activity. + * This function deals with any pending websocket traffic, + * so you need to call this function periodically. + * + * @param[in] context ico_uws context + * @return none + */ +/*--------------------------------------------------------------------------*/ +void ico_uws_service(struct ico_uws_context *context); + +/*--------------------------------------------------------------------------*/ +/** + * @brief ico_uws_get_uri + * Get the uri that is connecting to now. + * + * @param[in] context ico_uws context + * @return uri + * @retval data of string success + * @retval NULL error + */ +/*--------------------------------------------------------------------------*/ +char *ico_uws_get_uri(struct ico_uws_context *context); + +/*--------------------------------------------------------------------------*/ +/** + * @brief ico_uws_get_ready_state + * Get the state of connection. + * + * @param[in] context ico_uws context + * @return state + * @retval >= 0 success + * @retval ICO_UWS_STATE_UNKNOWN error + */ +/*--------------------------------------------------------------------------*/ +ico_uws_state_e ico_uws_get_ready_state(struct ico_uws_context *context); + +/*--------------------------------------------------------------------------*/ +/** + * @brief ico_uws_set_event_cb + * Set the event callback function. + * + * @param[in] context ico_uws context + * @param[in] callback callback function + * @param[in] user_data user data + * @return result + * @retval ICO_UWS_ERR_NONE success + * @retval others error + */ +/*--------------------------------------------------------------------------*/ +int ico_uws_set_event_cb(struct ico_uws_context *context, ico_uws_evt_cb callback, + void *user_data); + +/*--------------------------------------------------------------------------*/ +/** + * @brief ico_uws_unset_event_cb + * Unset the event callback function. + * + * @param[in] context ico_uws context + * @return none + */ +/*--------------------------------------------------------------------------*/ +void ico_uws_unset_event_cb(struct ico_uws_context *context); + + +#ifdef __cplusplus +} +#endif + +#endif /* __ICO_UWS_H__ */ diff --git a/include/ico_uws_private.h b/include/ico_uws_private.h new file mode 100644 index 0000000..dec4dc1 --- /dev/null +++ b/include/ico_uws_private.h @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2013, TOYOTA MOTOR CORPORATION. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ +/** + * @brief private header file of library for communicate + * + * @date June-7-2013 + */ + +#ifndef __ICO_UWS_PRIVATE_H__ +#define __ICO_UWS_PRIVATE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/*===========================================================================*/ +/* variable & table */ +/*===========================================================================*/ +#define ICO_UWS_MAX_NUM 200 /**< max num of list's data */ +#define ICO_UWS_MAX_FDS 4 /**< max num of file descriptors */ + +/* libwebsockets's protocol number */ +enum ico_apf_protocols +{ + PROTOCOL_HTTP = 0, /* always first */ + PROTOCOL_ICO_UWS, /* for ico_uws communication */ + PROTOCOL_END /* final */ +}; + +/* structure for managing the ico_uws_context */ +struct ico_uws_mng_context { + struct ico_uws_mng_context *next; + struct ico_uws_context *context; +}; + +/* structure for managing the ico_uws_context's list */ +struct ico_uws_mng_context_list { + struct ico_uws_mng_context *first; + struct ico_uws_mng_context *last; + struct ico_uws_mng_context *free; +}; + +/* structure of connection info */ +struct ico_uws_connect { + struct ico_uws_connect *next; + struct libwebsocket *wsi; +}; + +/* ico uws context */ +struct ico_uws_context { + struct libwebsocket_context *ws_context; + + /* connection information list */ + struct ico_uws_connect *con_list_first; + struct ico_uws_connect *con_list_last; + struct ico_uws_connect *con_list_free; + + char uri[128]; + int port; + ico_uws_state_e state; + struct { + int fd; + void *wsi; + } callback_fd_list[ICO_UWS_MAX_FDS]; + + struct libwebsocket_protocols protocols[PROTOCOL_END + 1]; + + ico_uws_evt_cb callback; + void *user_data; +}; + +/*============================================================================*/ +/* global API */ +/*============================================================================*/ +#if defined(__GNUC__) && __GNUC__ >= 4 +#define ICO_API __attribute__ ((visibility("default"))) +#else +#define ICO_API +#endif + +/*============================================================================*/ +/* log macro */ +/*============================================================================*/ +#ifndef _NO_USE_DLOG + +#ifdef LOG_TAG +#undef LOG_TAG +#endif + +#define LOG_TAG "ICO_UWS" +#include <dlog.h> + +static int _ico_uws_debug = 0; + +#define _ERR(fmt, arg...) \ + do { \ + fprintf(stderr, "ico_uws E: %s:%d [ "fmt" ]\n", \ + __FUNCTION__, \ + __LINE__, \ + ##arg); \ + LOGE("%s:%d " fmt, __FUNCTION__, __LINE__, ##arg); \ + } while (0) + +#define _WARN(fmt, arg...) \ + do { \ + LOGW("%s:%d " fmt, __FUNCTION__, __LINE__, ##arg); \ + } while (0) + +#define _INFO(fmt, arg...) \ + do { \ + LOGI("%s:%d " fmt, __FUNCTION__, __LINE__, ##arg); \ + } while (0) + +#define _DBG(fmt, arg...) \ + do { \ + if (_ico_uws_debug == 0) { \ + if (getenv("ICO_UWS_DEBUG")) \ + _ico_uws_debug = 1; \ + else \ + _ico_uws_debug = -1; \ + } \ + if (_ico_uws_debug > 0) { \ + LOGD("%s:%d " fmt, __FUNCTION__, __LINE__, ##arg); \ + } \ + } while (0) + +#else + +#define _ERR(fmt, arg...) \ + do { \ + fprintf(stderr, \ + "ico_uws E: %s:%d [ "fmt" ]\n", \ + __FUNCTION__, \ + __LINE__, \ + ##arg); \ + } while (0) + +#define _WARN(fmt, arg...) \ + do { \ + fprintf(stderr, \ + "ico_uws W: %s:%d [ "fmt" ]\n", \ + __FUNCTION__, \ + __LINE__, \ + ##arg); \ + } while (0) + + +#define _INFO(fmt, arg...) \ + do { \ + fprintf(stderr, \ + "ico_uws I: %s:%d [ "fmt" ]\n", \ + __FUNCTION__, \ + __LINE__, \ + ##arg); \ + } while (0) + +#define _DBG(fmt, arg...) \ + do { \ + if (getenv("ICO_UWS_DEBUG")) { \ + fprintf(stderr, \ + "ico_uws D: %s:%d [ "fmt" ]\n", \ + __FUNCTION__, \ + __LINE__, \ + ##arg); \ + } \ + } while (0) + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __ICO_UWS_PRIVATE_H__ */ diff --git a/packaging/ico-uxf-utilities.changes b/packaging/ico-uxf-utilities.changes new file mode 100644 index 0000000..16fe129 --- /dev/null +++ b/packaging/ico-uxf-utilities.changes @@ -0,0 +1,2 @@ +* Wed Jul 17 2013 Shibata Makoto <shibata@mac.tec.toyota.co.jp> f508b53 +- Import initial. diff --git a/packaging/ico-uxf-utilities.spec b/packaging/ico-uxf-utilities.spec new file mode 100644 index 0000000..3432960 --- /dev/null +++ b/packaging/ico-uxf-utilities.spec @@ -0,0 +1,58 @@ +Name: ico-uxf-utilities +Summary: common utilities for ico uifw +Version: 0.2.01 +Release: 1.1 +Group: TO_BE/FILLED_IN +License: Apache License, Version 2.0 +URL: "" +Source0: %{name}-%{version}.tar.bz2 + +BuildRequires: libwebsockets-devel >= 1.2 +BuildRequires: libdlog-devel +BuildRequires: pkgconfig(glib-2.0) +Requires: libwebsockets >= 1.2 +Requires: libdlog + +%description +common utilities for ico uifw. + +%package devel +Summary: Development files for %{name} +Group: Development/Utility/Libraries +Requires: %{name} = %{version}-%{release} +Requires: libwebsockets-devel + +%description devel +Development files for inter application communications. + +%prep +%setup -q -n %{name}-%{version} + +%build +autoreconf --install + +%autogen + +%configure +make %{?_smp_mflags} + +%install +rm -rf %{buildroot} +%make_install + +mkdir -p %{buildroot}/usr/lib/ + +# include +mkdir -p %{buildroot}/%{_includedir}/ico-util/ +cp -f include/ico_uws.h %{buildroot}/%{_includedir}/ico-util/ + +%post + +%files + +%{_libdir}/*.so.* + +%files devel +%defattr(-,root,root,-) +%{_includedir}/ico-util/ico_uws.h +%{_libdir}/*.so diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..63c73e1 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,17 @@ +lib_LTLIBRARIES = libico-util-com.la + +libico_util_com_la_CFLAGS = -I../include $(OPT_CFLAGS) +libico_util_com_la_LIBADD = $(OPT_LIBS) -lwebsockets +libico_util_com_la_LDFLAGS = -version-info 0:1:0 +libico_util_com_la_SOURCES = \ + ico_uws.c + +AM_CFLAGS = $(GCC_CFLAGS) +AM_CPPFLAGS = $(GCC_CFLAGS) \ + -DDATADIR='"$(datadir)"' \ + -DMODULEDIR='"$(moduledir)"' \ + -DLIBEXECDIR='"$(libexecdir)"' +AM_LDFLAGS = -module -avoid-version -rpath $(libdir) -lwebsockets + +.FORCE : + diff --git a/src/ico_uws.c b/src/ico_uws.c new file mode 100644 index 0000000..eb36701 --- /dev/null +++ b/src/ico_uws.c @@ -0,0 +1,1410 @@ +/* + * Copyright (c) 2013, TOYOTA MOTOR CORPORATION. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ +/** + * @brief library for communicate + * + * @date June-26-2013 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> +#include <unistd.h> + +#include <libwebsockets.h> +#include "ico_uws.h" +#include "ico_uws_private.h" + +/*===========================================================================*/ +/* definition */ +/*===========================================================================*/ +#define URI_WS "ws://" +#define URI_WS_SECURE "wss://" + +/* buffer of reserved data that failed to send */ +#define DATA_LEN LWS_SEND_BUFFER_PRE_PADDING + 1024 + \ + LWS_SEND_BUFFER_POST_PADDING + +/*===========================================================================*/ +/* define static function prototype */ +/*===========================================================================*/ +static struct ico_uws_connect *add_ws_instance(struct ico_uws_context *context, + struct libwebsocket *wsi); +static void del_ws_instance(struct ico_uws_context *context, + const struct libwebsocket *wsi); +static void del_connect_list(struct ico_uws_context *context); +static int add_context(struct ico_uws_context *context); +static struct ico_uws_context *get_context_by_wsctx( + struct libwebsocket_context *ws_context); +static struct ico_uws_context *get_context_by_wsi(struct libwebsocket *wsi); +static void del_context(struct ico_uws_context *context); +static void del_context_list(void); +static int exec_callback(const struct ico_uws_context *context, + const ico_uws_evt_e event, + const void *id, + const ico_uws_detail *detail, + const void *user_data); +static int server_http_callback(struct libwebsocket_context *ws_context, + struct libwebsocket *wsi, + enum libwebsocket_callback_reasons reason, + void *user, void *in, size_t len); +static int client_http_callback(struct libwebsocket_context *ws_context, + struct libwebsocket *wsi, + enum libwebsocket_callback_reasons reason, + void *user, void *in, size_t len); +static int server_uws_callback(struct libwebsocket_context *ws_context, + struct libwebsocket *wsi, + enum libwebsocket_callback_reasons reason, + void *user, void *in, size_t len); +static int client_uws_callback(struct libwebsocket_context *ws_context, + struct libwebsocket *wsi, + enum libwebsocket_callback_reasons reason, + void *user, void *in, size_t len); +static struct ico_uws_context *create_server(const char *uri, + const char *protocol); +static struct ico_uws_context *create_client(const char *uri, + const char *protocol); + +/*===========================================================================*/ +/* variable & table */ +/*===========================================================================*/ +/* ico_uws_context */ +struct context_info_t { + struct context_info_t *next; + struct ico_uws_context *context; +}; +static struct context_info_t *ctx_list_first = NULL; +static struct context_info_t *ctx_list_last = NULL; +static struct context_info_t *ctx_list_free = NULL; + +/* pthread mutex initialize */ +static pthread_mutex_t in_out_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t creating_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* current creating context */ +static struct ico_uws_context *current_creating_context = NULL; + +/*===========================================================================*/ +/* static function */ +/*===========================================================================*/ +/*--------------------------------------------------------------------------*/ +/** + * @brief add_ws_instance + * Add wsi into the ico_uws_connect list. + * + * @param[in] context ico_uws context + * @param[in] wsi libwebsocket instance + * @return ico_uws_connect pointer + * @retval pointer of list success + * @retval NULL error + */ +/*--------------------------------------------------------------------------*/ +static struct ico_uws_connect * +add_ws_instance(struct ico_uws_context *context, struct libwebsocket *wsi) +{ + int id; + struct ico_uws_connect *con_table; + ico_uws_detail detail; + + _DBG("add websocket instance called"); + if (context == NULL || wsi == NULL) { + _WARN("context or wsi is NULL"); + return NULL; + } + + /* mutex lock */ + pthread_mutex_lock(&in_out_mutex); + con_table = context->con_list_first; + /* search free table */ + for (id = 0; id < ICO_UWS_MAX_NUM; id++) { + if (con_table == NULL) break; + con_table = con_table->next; + } + + /* list is full */ + if (id == ICO_UWS_MAX_NUM) { + /* mutex unlock */ + pthread_mutex_unlock(&in_out_mutex); + detail._ico_uws_error.code = ICO_UWS_ERR_UNKNOWN; + _ERR("list is full (%d)", detail._ico_uws_error.code); + exec_callback(NULL, ICO_UWS_EVT_ERROR, NULL, &detail, NULL); + return NULL; + } + + /* create and set wsi */ + if (context->con_list_free == NULL) { + /* free table does not exist */ + con_table = calloc(1, sizeof(struct ico_uws_connect)); + if (con_table == NULL) { + /* mutex unlock */ + pthread_mutex_unlock(&in_out_mutex); + detail._ico_uws_error.code = ICO_UWS_ERR_OUT_OF_MEMORY; + _ERR("no memory for ico_uws_connect (%d)", + detail._ico_uws_error.code); + exec_callback(NULL, ICO_UWS_EVT_ERROR, NULL, &detail, NULL); + return NULL; + } + } + else { + /* use free area */ + con_table = context->con_list_free; + context->con_list_free = con_table->next; + } + /* add wsi */ + con_table->wsi = wsi; + _DBG("add wsi to the connect info table success"); + + /* add table to list */ + con_table->next = NULL; + if (context->con_list_first == NULL) { + context->con_list_first = con_table; + } + else { + context->con_list_last->next = con_table; + } + context->con_list_last = con_table; + /* mutex unlock */ + pthread_mutex_unlock(&in_out_mutex); + + return con_table; +} + +/*--------------------------------------------------------------------------*/ +/** + * @brief del_ws_instance + * Delete wsi from the ico_uws_connect list. + * + * @param[in] context ico_uws context + * @param[in] wsi libwebsocket instance + * @return none + */ +/*--------------------------------------------------------------------------*/ +static void +del_ws_instance(struct ico_uws_context *context, + const struct libwebsocket *wsi) +{ + int id; + struct ico_uws_connect *con_table, *prev_con_table; + + if (context == NULL || wsi == NULL) { + _WARN("context or wsi is NULL"); + return; + } + + /* mutex lock */ + pthread_mutex_lock(&in_out_mutex); + con_table = context->con_list_first; + prev_con_table = NULL; + /* search wsi */ + for (id = 0; id < ICO_UWS_MAX_NUM; id++) { + if (con_table == NULL) break; + if (con_table->wsi == wsi) { + /* update list */ + if (prev_con_table == NULL) { + context->con_list_first = con_table->next; + } + else { + prev_con_table->next = con_table->next; + } + /* update list's last */ + if (context->con_list_last == con_table) { + context->con_list_last = prev_con_table; + } + /* clear data */ + con_table->wsi = NULL; + _DBG("delete wsi from the connect info table success"); + /* add to free list */ + con_table->next = context->con_list_free; + context->con_list_free = con_table; + break; + } + prev_con_table = con_table; + con_table = con_table->next; + } + + /* mutex unlock */ + pthread_mutex_unlock(&in_out_mutex); + + return; +} + +/*--------------------------------------------------------------------------*/ +/** + * @brief del_connect_list + * Delete connect information list. + * + * @param[in] context ico_uws context + * @return none + */ +/*--------------------------------------------------------------------------*/ +static void +del_connect_list(struct ico_uws_context *context) +{ + int id; + struct ico_uws_connect *con_table; + + _DBG("delete connect information list"); + /* mutex lock */ + pthread_mutex_lock(&in_out_mutex); + for (id = 0; id < ICO_UWS_MAX_NUM; id++) { + con_table = context->con_list_first; + if (con_table == NULL) break; + if (con_table->wsi != NULL) con_table->wsi = NULL; + context->con_list_first = con_table->next; + free(con_table); + } + + for (id = 0; id < ICO_UWS_MAX_NUM; id++) { + con_table = context->con_list_free; + if (con_table == NULL) break; + if (con_table->wsi != NULL) con_table->wsi = NULL; + context->con_list_free = con_table->next; + free(con_table); + } + /* mutex unlock */ + pthread_mutex_unlock(&in_out_mutex); + + return; +} + +/*--------------------------------------------------------------------------*/ +/** + * @brief add_context + * Add ico_uws context to the local list. + * + * @param[in] context ico_uws context + * @return result + * @retval ICO_UWS_ERR_NONE success + * @retval < 0 error + */ +/*--------------------------------------------------------------------------*/ +static int +add_context(struct ico_uws_context *context) +{ + int id; + struct context_info_t *ctx_table; + ico_uws_detail detail; + + if (context == NULL) { + detail._ico_uws_error.code = ICO_UWS_ERR_INVALID_PARAM; + _ERR("invalid param (%d)", detail._ico_uws_error.code); + exec_callback(NULL, ICO_UWS_EVT_ERROR, NULL, &detail, NULL); + return detail._ico_uws_error.code; + } + + /* mutex lock */ + pthread_mutex_lock(&in_out_mutex); + ctx_table = ctx_list_first; + /* search free table */ + for (id = 0; id < ICO_UWS_MAX_NUM; id++) { + if (ctx_table == NULL) break; + ctx_table = ctx_table->next; + } + + /* local list is full */ + if (id == ICO_UWS_MAX_NUM) { + /* mutex unlock */ + pthread_mutex_unlock(&in_out_mutex); + detail._ico_uws_error.code = ICO_UWS_ERR_UNKNOWN; + _ERR("list is full (%d)", detail._ico_uws_error.code); + exec_callback(NULL, ICO_UWS_EVT_ERROR, NULL, &detail, NULL); + return detail._ico_uws_error.code; + } + + /* create and set context */ + if (ctx_list_free == NULL) { + /* free table does not exist */ + ctx_table = calloc(1, sizeof(struct context_info_t)); + if (ctx_table == NULL) { + /* mutex unlock */ + pthread_mutex_unlock(&in_out_mutex); + detail._ico_uws_error.code = ICO_UWS_ERR_OUT_OF_MEMORY; + _ERR("no memory for ico_uws_connect (%d)", + detail._ico_uws_error.code); + exec_callback(NULL, ICO_UWS_EVT_ERROR, NULL, &detail, NULL); + return detail._ico_uws_error.code; + } + } + else { + /* use free area */ + ctx_table = ctx_list_free; + ctx_list_free = ctx_table->next; + } + /* add data */ + ctx_table->context = context; + _DBG("add context to the local list success"); + + /* add table to list */ + ctx_table->next = NULL; + if (ctx_list_first == NULL) { + ctx_list_first = ctx_table; + } + else { + ctx_list_last->next = ctx_table; + } + ctx_list_last = ctx_table; + /* mutex unlock */ + pthread_mutex_unlock(&in_out_mutex); + + return ICO_UWS_ERR_NONE; +} + +/*--------------------------------------------------------------------------*/ +/** + * @brief get_context_by_wsctx + * Get ico_uws_context from the local list by websocket context. + * + * @param[in] ws_context libwebsocket context + * @return ico_uws context + * @retval context ico_uws context's address + * @retval NULL error + */ +/*--------------------------------------------------------------------------*/ +static struct ico_uws_context * +get_context_by_wsctx(struct libwebsocket_context *ws_context) +{ + struct context_info_t *ctx_table; + struct ico_uws_context *context; + int id; + + context = NULL; + /* mutex lock */ + pthread_mutex_lock(&in_out_mutex); + ctx_table = ctx_list_first; + for (id = 0; id < ICO_UWS_MAX_NUM; id++) { + if (ctx_table == NULL) { + /* mutex unlock */ + pthread_mutex_unlock(&in_out_mutex); + _DBG("ws_context(0x%08x) does not exist in the local list", (int)ws_context); + return current_creating_context; + } + if (ctx_table->context->ws_context == ws_context) { + context = ctx_table->context; + _DBG("get context success (ctx: 0x%08x)", (int)context); + break; + } + ctx_table = ctx_table->next; + } + /* mutex unlock */ + pthread_mutex_unlock(&in_out_mutex); + + return context; +} + +/*--------------------------------------------------------------------------*/ +/** + * @brief get_context_by_wsi + * Get ico_uws_context from the local list by websocket instance. + * + * @param[in] wsi libwebsocket instance + * @return ico_uws context + * @retval context ico_uws context's address + * @retval NULL error + */ +/*--------------------------------------------------------------------------*/ +static struct ico_uws_context * +get_context_by_wsi(struct libwebsocket *wsi) +{ + struct context_info_t *ctx_table; + struct ico_uws_connect *con_table; + struct ico_uws_context *context; + int id, con_id; + + context = NULL; + /* mutex lock */ + pthread_mutex_lock(&in_out_mutex); + ctx_table = ctx_list_first; + for (id = 0; id < ICO_UWS_MAX_NUM; id++) { + if (ctx_table == NULL) { + /* mutex unlock */ + pthread_mutex_unlock(&in_out_mutex); + _DBG("context does not exist in the local list"); + return current_creating_context; + } + con_table = ctx_table->context->con_list_first; + for (con_id = 0; con_id < ICO_UWS_MAX_NUM; con_id++) { + if (con_table == NULL) break; + if (con_table->wsi == wsi) { + context = ctx_table->context; + _DBG("get context success (ctx: 0x%08x)", (int)context); + break; + } + con_table = con_table->next; + } + if (context != NULL) break; + ctx_table = ctx_table->next; + } + /* mutex unlock */ + pthread_mutex_unlock(&in_out_mutex); + + return context; +} + +/*--------------------------------------------------------------------------*/ +/** + * @brief del_context + * Delete the uws context's table from the local list. + * + * @param[in] context ico_uws context + * @return none + */ +/*--------------------------------------------------------------------------*/ +static void +del_context(struct ico_uws_context *context) +{ + int id; + struct context_info_t *ctx_table, *prev_ctx_table; + + if (context == NULL) { + _WARN("context is NULL"); + return; + } + + /* mutex lock */ + pthread_mutex_lock(&in_out_mutex); + ctx_table = ctx_list_first; + prev_ctx_table = NULL; + /* search wsi */ + for (id = 0; id < ICO_UWS_MAX_NUM; id++) { + if (ctx_table == NULL) break; + if (ctx_table->context == context) { + /* update list */ + if (prev_ctx_table == NULL) { + ctx_list_first = ctx_table->next; + } + else { + prev_ctx_table->next = ctx_table->next; + } + /* update list's last */ + if (ctx_list_last == ctx_table) { + ctx_list_last = prev_ctx_table; + } + /* clear data */ + free(ctx_table->context); + ctx_table->context = NULL; + _DBG("delete context success"); + /* add to free list */ + ctx_table->next = ctx_list_free; + ctx_list_free = ctx_table; + break; + } + prev_ctx_table = ctx_table; + ctx_table = ctx_table->next; + } + + /* mutex unlock */ + pthread_mutex_unlock(&in_out_mutex); + + return; +} + +/*--------------------------------------------------------------------------*/ +/** + * @brief del_context_list + * Delete the local uws context's list. + * + * @param none + * @return none + */ +/*--------------------------------------------------------------------------*/ +static void +del_context_list(void) +{ + int id; + struct context_info_t *ctx_table; + + _DBG("delete context info list"); + /* mutex lock */ + pthread_mutex_lock(&in_out_mutex); + for (id = 0; id < ICO_UWS_MAX_NUM; id++) { + ctx_table = ctx_list_first; + if (ctx_table == NULL) break; + + ctx_list_first = ctx_table->next; + free(ctx_table); + _DBG("delete context info table success"); + } + + for (id = 0; id < ICO_UWS_MAX_NUM; id++) { + ctx_table = ctx_list_free; + if (ctx_table == NULL) break; + + ctx_list_free = ctx_table->next; + free(ctx_table); + _DBG("delete context info free table success"); + } + /* mutex unlock */ + pthread_mutex_unlock(&in_out_mutex); + + return; +} + +/*--------------------------------------------------------------------------*/ +/** + * @brief exec_callback + * Execute callback function. + * + * @param[in] context ico_uws context + * @param[in] event event code + * @param[in] id unique id + * @param[in] detail detail information + * @param[in] user_data user data + * @return result + * @retval ICO_UWS_ERR_NONE success (called callback function) + * @retval ICO_UWS_ERR_INVALID_PARAM error (no context or no callback function) + */ +/*--------------------------------------------------------------------------*/ +static int +exec_callback(const struct ico_uws_context *context, + const ico_uws_evt_e event, + const void *id, const ico_uws_detail *detail, + const void *user_data) +{ + if (context == NULL || context->callback == NULL) { + _DBG("callback function does not exist"); + /* always success */ + return ICO_UWS_ERR_CONNECT; + } + + _DBG("exec_callback (event: %d)", event); + context->callback(context, event, id, detail, context->user_data); + + /* always success */ + return ICO_UWS_ERR_NONE; +} + +/*--------------------------------------------------------------------------*/ +/** + * @brief server_http_callback + * HTTP server callback function. + * + * @param[in] ws_context libwebsocket context + * @param[in] wsi libwebsocket instance + * @param[in] reason callback reason + * @param[in] user user data + * @param[in] in data (used for some callback reasons) + * @param[in] len size (used for some callback reasons) + * @return result + * @retval ICO_UWS_ERR_NONE success (this function is always success) + */ +/*--------------------------------------------------------------------------*/ +static int +server_http_callback(struct libwebsocket_context *ws_context, + struct libwebsocket *wsi, + enum libwebsocket_callback_reasons reason, + void *user, void *in, size_t len) +{ + struct ico_uws_context *context = get_context_by_wsctx(ws_context); + ico_uws_detail detail; + int i; + + switch (reason) { + case LWS_CALLBACK_FILTER_NETWORK_CONNECTION: + _DBG("server http cb: filter network connection"); + break; + case LWS_CALLBACK_ADD_POLL_FD: + detail._ico_uws_fd.fd = libwebsocket_get_socket_fd(wsi); + _DBG("server http cb: add connection socket (%d)", detail._ico_uws_fd.fd); + if ((exec_callback(context, ICO_UWS_EVT_ADD_FD, (void *)wsi, &detail, NULL) + != ICO_UWS_ERR_NONE) && (context != NULL)) { + for (i = 0; i < ICO_UWS_MAX_FDS; i++) { + if (context->callback_fd_list[i].fd == 0) break; + } + if (i < ICO_UWS_MAX_FDS) { + context->callback_fd_list[i].fd = detail._ico_uws_fd.fd; + context->callback_fd_list[i].wsi = (void *)wsi; + } + } + break; + case LWS_CALLBACK_DEL_POLL_FD: + detail._ico_uws_fd.fd = libwebsocket_get_socket_fd(wsi); + _DBG("server http cb: delete connection socket (%d)", detail._ico_uws_fd.fd); + if (context) { + for (i = 0; i < ICO_UWS_MAX_FDS; i++) { + if (context->callback_fd_list[i].fd == detail._ico_uws_fd.fd) { + context->callback_fd_list[i].fd = 0; + } + } + } + exec_callback(context, ICO_UWS_EVT_DEL_FD, (void *)wsi, + &detail, NULL); + break; + default: + _DBG("server http cb: unhandled callback: %d", reason); + break; + } + + /* always success */ + return ICO_UWS_ERR_NONE; +} + +/*--------------------------------------------------------------------------*/ +/** + * @brief client_http_callback + * HTTP client callback function. + * + * @param[in] ws_context libwebsocket context + * @param[in] wsi libwebsocket instance + * @param[in] reason callback reason + * @param[in] user user data + * @param[in] in data (used for some callback reasons) + * @param[in] len size (used for some callback reasons) + * @return result + * @retval ICO_UWS_ERR_NONE success (this function is always success) + */ +/*--------------------------------------------------------------------------*/ +static int +client_http_callback(struct libwebsocket_context *ws_context, + struct libwebsocket *wsi, + enum libwebsocket_callback_reasons reason, + void *user, void *in, size_t len) +{ + struct ico_uws_context *context = get_context_by_wsi(wsi); + ico_uws_detail detail; + int fd; + int i; + + switch (reason) { + case LWS_CALLBACK_FILTER_NETWORK_CONNECTION: + _DBG("client http cb: filter network connection"); + break; + case LWS_CALLBACK_ADD_POLL_FD: + fd = libwebsocket_get_socket_fd(wsi); + detail._ico_uws_fd.fd = fd; + _DBG("client http cb: add connection socket (%d)", detail._ico_uws_fd.fd); + if ((exec_callback(context, ICO_UWS_EVT_ADD_FD, (void *)wsi, &detail, NULL) + != ICO_UWS_ERR_NONE) && (context != NULL)) { + for (i = 0; i < ICO_UWS_MAX_FDS; i++) { + if (context->callback_fd_list[i].fd == 0) break; + } + if (i < ICO_UWS_MAX_FDS) { + context->callback_fd_list[i].fd = detail._ico_uws_fd.fd; + context->callback_fd_list[i].wsi = (void *)wsi; + } + } + break; + case LWS_CALLBACK_DEL_POLL_FD: + fd = libwebsocket_get_socket_fd(wsi); + detail._ico_uws_fd.fd = fd; + _DBG("client http cb: delete connection socket (%d)", detail._ico_uws_fd.fd); + if (context) { + for (i = 0; i < ICO_UWS_MAX_FDS; i++) { + if (context->callback_fd_list[i].fd == detail._ico_uws_fd.fd) { + context->callback_fd_list[i].fd = 0; + } + } + } + exec_callback(context, ICO_UWS_EVT_DEL_FD, (void *)wsi, + &detail, NULL); + break; + default: + _DBG("client http cb: unhandled callback: %d", reason); + break; + } + + /* always success */ + return ICO_UWS_ERR_NONE; +} + +/*--------------------------------------------------------------------------*/ +/** + * @brief server_uws_callback + * Server callback function for ico_uws. + * + * @param[in] ws_context libwebsocket context + * @param[in] wsi libwebsocket instance + * @param[in] reason callback reason + * @param[in] user user data + * @param[in] in data (used for some callback reasons) + * @param[in] len size (used for some callback reasons) + * @return result + * @retval ICO_UWS_ERR_NONE success + * @retval ICO_UWS_ERR_CLOSED connection closed + */ +/*--------------------------------------------------------------------------*/ +static int +server_uws_callback(struct libwebsocket_context *ws_context, + struct libwebsocket *wsi, + enum libwebsocket_callback_reasons reason, + void *user, void *in, size_t len) +{ + struct ico_uws_context *context = get_context_by_wsctx(ws_context); + ico_uws_detail detail; + int ret = ICO_UWS_ERR_NONE; + + switch (reason) { + case LWS_CALLBACK_ESTABLISHED: + /* server connection is established */ + _DBG("server cb: established"); + if (context != NULL) { + add_ws_instance(context, wsi); + context->state = ICO_UWS_STATE_OPEN; + } + exec_callback(context, ICO_UWS_EVT_OPEN, (void *)wsi, + NULL, NULL); + (void)libwebsocket_callback_on_writable(ws_context, wsi); + break; + case LWS_CALLBACK_RECEIVE: + /* receive data */ + _DBG("server cb: receive data"); + detail._ico_uws_message.recv_data = in; + detail._ico_uws_message.recv_len = len; + exec_callback(context, ICO_UWS_EVT_RECEIVE, (void *)wsi, + &detail, NULL); + break; + case LWS_CALLBACK_SERVER_WRITEABLE: + _DBG("server cb: server writable"); + break; + case LWS_CALLBACK_CLOSED: + /* websocket session ends */ + _DBG("server cb: websocket session ends"); + if (context != NULL) { + context->state = ICO_UWS_STATE_CLOSED; + del_ws_instance(context, wsi); + } + exec_callback(context, ICO_UWS_EVT_CLOSE, (void *)wsi, NULL, NULL); + ret = ICO_UWS_ERR_CLOSED; + break; + default: + /* unhandled callback reason */ + _DBG("server cb: unhandled callback %d", reason); + break; + } + + return ret; +} + +/*--------------------------------------------------------------------------*/ +/** + * @brief client_uws_callback + * Client callback function for ico_uws. + * + * @param[in] ws_context libwebsocket context + * @param[in] wsi libwebsocket instance + * @param[in] reason callback reason + * @param[in] user user data + * @param[in] in data (used for some callback reasons) + * @param[in] len size (used for some callback reasons) + * @return result + * @retval ICO_UWS_ERR_NONE success + * @retval ICO_UWS_ERR_CLOSED connection closed + */ +/*--------------------------------------------------------------------------*/ +static int +client_uws_callback(struct libwebsocket_context *ws_context, + struct libwebsocket *wsi, + enum libwebsocket_callback_reasons reason, + void *user, void *in, size_t len) +{ + struct ico_uws_context *context = get_context_by_wsi(wsi); + ico_uws_detail detail; + int ret = ICO_UWS_ERR_NONE; + + switch (reason) { + case LWS_CALLBACK_CLIENT_ESTABLISHED: + /* client connection is established */ + _DBG("client cb: client established"); + if (context != NULL) { + add_ws_instance(context, wsi); + context->state = ICO_UWS_STATE_OPEN; + } + exec_callback(context, ICO_UWS_EVT_OPEN, (void *)wsi, + NULL, NULL); + (void)libwebsocket_callback_on_writable(ws_context, wsi); + break; + case LWS_CALLBACK_CLIENT_RECEIVE: + /* receive data */ + _DBG("client cb: client receive"); + detail._ico_uws_message.recv_data = in; + detail._ico_uws_message.recv_len = len; + exec_callback(context, ICO_UWS_EVT_RECEIVE, (void *)wsi, + &detail, NULL); + break; + case LWS_CALLBACK_CLIENT_WRITEABLE: + _DBG("client cb: client writable"); + break; + case LWS_CALLBACK_CLOSED: + /* websocket session ends */ + _DBG("client cb: websocket session ends"); + if (context != NULL) { + context->state = ICO_UWS_STATE_CLOSED; + del_ws_instance(context, wsi); + } + exec_callback(context, ICO_UWS_EVT_CLOSE, (void *)wsi, NULL, NULL); + ret = ICO_UWS_ERR_CLOSED; + break; + default: + _DBG("client cb: unhandled callback %d", reason); + break; + } + + return ret; +} + +/*--------------------------------------------------------------------------*/ +/** + * @brief create_server + * Create server's ico_uws context. + * + * @param[in] uri the uri to which to connect + * @param[in] protocol the protocol name + * @return context + * @retval ico_uws context success + * @retval NULL error + */ +/*--------------------------------------------------------------------------*/ +static struct ico_uws_context * +create_server(const char *uri, const char *protocol) +{ + struct lws_context_creation_info ws_info; + struct ico_uws_context *srv_context = NULL; + ico_uws_detail detail; + char *cpy_uri = NULL; + int port = 0; + int id; + + _DBG("create_server (uri: %s)", uri); + /* create ico_uws_context */ + srv_context = (struct ico_uws_context *) + malloc(sizeof(struct ico_uws_context)); + if (srv_context == NULL) { + detail._ico_uws_error.code = ICO_UWS_ERR_OUT_OF_MEMORY; + _ERR("no memory for ico_uws_context (%d)", + detail._ico_uws_error.code); + exec_callback(NULL, ICO_UWS_EVT_ERROR, NULL, &detail, NULL); + return NULL; + } + memset(srv_context, 0, sizeof(*srv_context)); + + /* set port number */ + cpy_uri = strdup((char *)uri); + port = atoi(&cpy_uri[1]); /* delete colon(:) */ + free(cpy_uri); + + /* set protocol info */ + for (id = PROTOCOL_HTTP; id <= PROTOCOL_END; id++) { + const char *name = NULL; + void *callback = NULL; + size_t size = 0; + switch (id) { + case PROTOCOL_HTTP: + name = "http_only"; + callback = server_http_callback; + break; + case PROTOCOL_ICO_UWS: + name = (const char *)strdup((char *)protocol); + callback = server_uws_callback; + break; + case PROTOCOL_END: + /* End of list's name and callback is NULL */ + default: + /* never reach here */ + break; + } + srv_context->protocols[id].name = name; + srv_context->protocols[id].callback = callback; + srv_context->protocols[id].per_session_data_size = size; + } + + /* clear libwebsocket info */ + memset(&ws_info, 0, sizeof(ws_info)); + /* set lws_context_creation_info */ + ws_info.port = port; + ws_info.iface = NULL; /* to bind the listen socket to all */ + ws_info.protocols = srv_context->protocols; + ws_info.extensions = libwebsocket_get_internal_extensions(); + ws_info.ssl_cert_filepath = NULL; + ws_info.ssl_private_key_filepath = NULL; + ws_info.ssl_ca_filepath = NULL; + ws_info.ssl_cipher_list = NULL; + ws_info.gid = -1; + ws_info.uid = -1; + ws_info.options = 0; /* no special options */ + ws_info.user = NULL; + ws_info.ka_time = 0; + ws_info.ka_probes = 0; + ws_info.ka_interval = 0; + + /* create a server context */ + pthread_mutex_lock(&creating_mutex); + current_creating_context = srv_context; + srv_context->ws_context = libwebsocket_create_context(&ws_info); + current_creating_context = NULL; + pthread_mutex_unlock(&creating_mutex); + + if (srv_context->ws_context == NULL) { + /* create context failed */ + detail._ico_uws_error.code = ICO_UWS_ERR_CREATE; + _ERR("libwebsocket create context failed (%d)", + detail._ico_uws_error.code); + exec_callback(NULL, ICO_UWS_EVT_ERROR, NULL, &detail, NULL); + free(srv_context); + return NULL; + } + /* set data */ + strcpy((char *)(srv_context->uri), uri); + srv_context->state = ICO_UWS_STATE_CONNECTING; + /* add to the local list */ + add_context(srv_context); + /* server created */ + _DBG("server created and listening on port %d", port); + + return srv_context; +} + +/*--------------------------------------------------------------------------*/ +/** + * @brief create_client + * Create client's ico_uws context. + * + * @param[in] uri the uri to which to connect + * @param[in] protocol the protocol name + * @return context + * @retval ico_uws context success + * @retval NULL error + */ +/*--------------------------------------------------------------------------*/ +static struct ico_uws_context * +create_client(const char *uri, const char *protocol) +{ + struct lws_context_creation_info ws_info; + struct ico_uws_context *clt_context = NULL; + ico_uws_detail detail; + char *split_mark = ":/"; + char *cpy_uri = NULL; + char *address = NULL; + int port = 0; + int id; + + struct libwebsocket *wsi; + int use_ssl = 0; /* not use ssl */ + int ietf_version = -1; /* -1: default value */ + char *host = NULL; /* host name */ + char *origin = NULL; /* socket name */ + + _DBG("create_client (uri: %s)", uri); + /* create ico_uws_context */ + clt_context = (struct ico_uws_context *) + malloc(sizeof(struct ico_uws_context)); + if (clt_context == NULL) { + detail._ico_uws_error.code = ICO_UWS_ERR_OUT_OF_MEMORY; + _ERR("no memory for ico_uws_context (%d)", detail._ico_uws_error.code); + exec_callback(NULL, ICO_UWS_EVT_ERROR, NULL, &detail, NULL); + return NULL; + } + memset(clt_context, 0, sizeof(*clt_context)); + + /* set protocol info */ + for (id = PROTOCOL_HTTP; id <= PROTOCOL_END; id++) { + const char *name = NULL; + void *callback = NULL; + size_t size = 0; + switch (id) { + case PROTOCOL_HTTP: + name = "http_only"; + callback = client_http_callback; + break; + case PROTOCOL_ICO_UWS: + name = (const char *)strdup((char *)protocol); + callback = client_uws_callback; + break; + case PROTOCOL_END: + /* End of list's name and callback is NULL */ + default: + /* never reach here */ + break; + } + clt_context->protocols[id].name = name; + clt_context->protocols[id].callback = callback; + clt_context->protocols[id].per_session_data_size = size; + } + + /* clear libwebsocket info */ + memset(&ws_info, 0, sizeof(ws_info)); + /* set lws_context_creation_info */ + ws_info.port = CONTEXT_PORT_NO_LISTEN; + ws_info.iface = NULL; /* to bind the listen socket to all */ + ws_info.protocols = clt_context->protocols; + ws_info.extensions = libwebsocket_get_internal_extensions(); + ws_info.ssl_cert_filepath = NULL; + ws_info.ssl_private_key_filepath = NULL; + ws_info.ssl_ca_filepath = NULL; + ws_info.ssl_cipher_list = NULL; + ws_info.gid = -1; + ws_info.uid = -1; + ws_info.options = 0; /* no special options */ + ws_info.user = NULL; + ws_info.ka_time = 0; + ws_info.ka_probes = 0; + ws_info.ka_interval = 0; + + /* create a client context */ + pthread_mutex_lock(&creating_mutex); + current_creating_context = clt_context; + + clt_context->ws_context = libwebsocket_create_context(&ws_info); + if (clt_context->ws_context == NULL) { + /* create context failed */ + current_creating_context = NULL; + pthread_mutex_unlock(&creating_mutex); + detail._ico_uws_error.code = ICO_UWS_ERR_CREATE; + _ERR("libwebsocket create context failed (%d)", + detail._ico_uws_error.code); + exec_callback(NULL, ICO_UWS_EVT_ERROR, NULL, &detail, NULL); + free(clt_context); + return NULL; + } + + /* alloc */ + cpy_uri = strdup((char *)uri); + /* split uri and set address, port */ + strtok(cpy_uri, split_mark); /* delet tag */ + address = strtok(NULL, split_mark); + port = atoi(strtok(NULL, split_mark)); + + /* create a client websocket instance */ + host = address; + wsi = libwebsocket_client_connect(clt_context->ws_context, + address, port, + use_ssl, "/", host, origin, + protocol, ietf_version); + current_creating_context = NULL; + pthread_mutex_unlock(&creating_mutex); + + clt_context->state = ICO_UWS_STATE_CONNECTING; + /* free */ + free(cpy_uri); + if (wsi == NULL) { + /* client connect failed */ + detail._ico_uws_error.code = ICO_UWS_ERR_CONNECT; + _ERR("libwebsocket client connect failed (%d)", + detail._ico_uws_error.code); + exec_callback(NULL, ICO_UWS_EVT_ERROR, NULL, &detail, NULL); + libwebsocket_context_destroy(clt_context->ws_context); + free(clt_context); + return NULL; + } + /* set data */ + add_ws_instance(clt_context, wsi); + strcpy((char *)(clt_context->uri), uri); + clt_context->state = ICO_UWS_STATE_OPEN; + /* add to the local list */ + add_context(clt_context); + /* client connected */ + _DBG("client connected address: %s, port: %d", address, port); + + return clt_context; +} + +/*===========================================================================*/ +/* public interface function */ +/*===========================================================================*/ +/*--------------------------------------------------------------------------*/ +/** + * @brief ico_uws_create_context + * Create ico_uws context. + * This API does not support secure access ("wss://") and + * the multi protocols. + * (If user sets "wss://", this function processes as "ws://".) + * + * @param[in] uri the uri to which to connect + * server sets the string ":(port)" + * client sets the string "ws://(host):(port)" + * @param[in] protocol the protocol name + * @return context + * @retval ico_uws context success + * @retval NULL error + */ +/*--------------------------------------------------------------------------*/ +ICO_API struct ico_uws_context * +ico_uws_create_context(const char *uri, const char *protocol) +{ + struct ico_uws_context *context; + ico_uws_detail detail; + + _DBG("ico_uws_create_context called"); + if (uri == NULL || protocol == NULL) { + detail._ico_uws_error.code = ICO_UWS_ERR_INVALID_PARAM; + _ERR("invalid param (%d)", detail._ico_uws_error.code); + exec_callback(NULL, ICO_UWS_EVT_ERROR, NULL, &detail, NULL); + return NULL; + } + + if (strncmp(uri, ":", 1) == 0) { + /* server */ + context = create_server(uri, protocol); + _DBG("ico_uws_create_context created server context 0x%08x", (int)context); + } + else if (strstr(uri, URI_WS) != NULL || strstr(uri, URI_WS_SECURE) != NULL) { + /* client */ + context = create_client(uri, protocol); + _DBG("ico_uws_create_context created client context 0x%08x", (int)context); + } + else { + detail._ico_uws_error.code = ICO_UWS_ERR_INVALID_PARAM; + _ERR("invalid uri (%d)", detail._ico_uws_error.code); + exec_callback(NULL, ICO_UWS_EVT_ERROR, NULL, &detail, NULL); + return NULL; + } + + return context; +} + +/*--------------------------------------------------------------------------*/ +/** + * @brief ico_uws_close + * Close the connection and destroy the ico_uws context. + * + * @param[in] context ico_uws context + * @return none + */ +/*--------------------------------------------------------------------------*/ +ICO_API void +ico_uws_close(struct ico_uws_context *context) +{ + ico_uws_detail detail; + + _DBG("ico_uws_close called"); + if (context == NULL) { + detail._ico_uws_error.code = ICO_UWS_ERR_INVALID_PARAM; + _ERR("invalid param (%d)", detail._ico_uws_error.code); + exec_callback(context, ICO_UWS_EVT_ERROR, NULL, &detail, NULL); + return; + } + + /* free list */ + del_connect_list(context); + + /* destroy websocket context */ + if (context->ws_context != NULL) { + (void)libwebsocket_context_destroy(context->ws_context); + } + + /* free "protocol name" area */ + if (context->protocols[PROTOCOL_ICO_UWS].name != NULL) { + free((char *)context->protocols[PROTOCOL_ICO_UWS].name); + } + + /* free ico_uws_context */ + (void)del_context(context); + if (ctx_list_first == NULL) { + /* no context exists in the local, but free list exists */ + (void)del_context_list(); + } + + return; +} + +/*--------------------------------------------------------------------------*/ +/** + * @brief ico_uws_send + * Send data to the connecting server or client. + * User needs to call the function "ico_uws_service" + * before calling this function. + * + * @param[in] context ico_uws context + * @param[in] id the id to connected to (callback notifies) + * @param[in] data the data to send + * @param[in] len count of the data bytes + * @return none + * @see ico_uws_service + */ +/*--------------------------------------------------------------------------*/ +ICO_API void +ico_uws_send(struct ico_uws_context *context, void *id, + unsigned char *data, size_t len) +{ + int ret = 0; + unsigned char buf[DATA_LEN] = {}; + struct libwebsocket *wsi; + ico_uws_detail detail; + + _DBG("ico_uws_send called"); + if (context == NULL || id == NULL || data == NULL || len <= 0) { + detail._ico_uws_error.code = ICO_UWS_ERR_INVALID_PARAM; + _ERR("invalid param (%d)", detail._ico_uws_error.code); + exec_callback(context, ICO_UWS_EVT_ERROR, id, &detail, NULL); + return; + } + + wsi = (struct libwebsocket *)id; + /* set send data to buffer */ + memcpy(&buf[LWS_SEND_BUFFER_PRE_PADDING], data, len); + /* send data using websocket instance */ + ret = libwebsocket_write(wsi, + &buf[LWS_SEND_BUFFER_PRE_PADDING], + len, LWS_WRITE_BINARY); + if (ret < 0) { + /* libwebsocket write failed */ + detail._ico_uws_error.code = ICO_UWS_ERR_SEND; + _ERR("libwebsocket write failed ret=%d (%d)", + ret, detail._ico_uws_error.code); + exec_callback(context, ICO_UWS_EVT_ERROR, id, &detail, NULL); + } + + return; +} + +/*--------------------------------------------------------------------------*/ +/** + * @brief ico_uws_service + * Service any pending websocket activity. + * This function deals with any pending websocket traffic, + * so you need to call this function periodically. + * + * @param[in] context ico_uws context + * @return none + */ +/*--------------------------------------------------------------------------*/ +ICO_API void +ico_uws_service(struct ico_uws_context *context) +{ + int timedout_ms = 0; + ico_uws_detail detail; + + if (context == NULL) { + detail._ico_uws_error.code = ICO_UWS_ERR_INVALID_PARAM; + _ERR("invalid param (%d)", detail._ico_uws_error.code); + exec_callback(context, ICO_UWS_EVT_ERROR, NULL, &detail, NULL); + return; + } + + /* service any pending websocket activity */ + (void)libwebsocket_service(context->ws_context, timedout_ms); + + return; +} + +/*--------------------------------------------------------------------------*/ +/** + * @brief ico_uws_get_uri + * Get the uri that is connecting to now. + * + * @param[in] context ico_uws context + * @return uri + * @retval data of string success + * @retval NULL error + */ +/*--------------------------------------------------------------------------*/ +ICO_API char * +ico_uws_get_uri(struct ico_uws_context *context) +{ + ico_uws_detail detail; + + _DBG("ico_uws_get_uri called"); + if (context == NULL) { + detail._ico_uws_error.code = ICO_UWS_ERR_INVALID_PARAM; + _ERR("invalid param (%d)", detail._ico_uws_error.code); + exec_callback(context, ICO_UWS_EVT_ERROR, NULL, &detail, NULL); + return NULL; + } + + _DBG("return uri: %s", context->uri); + return context->uri; +} + +/*--------------------------------------------------------------------------*/ +/** + * @brief ico_uws_get_ready_state + * Get the state of connection. + * + * @param[in] context ico_uws context + * @return state + * @retval >= 0 success + * @retval ICO_UWS_STATE_UNKNOWN error + */ +/*--------------------------------------------------------------------------*/ +ICO_API ico_uws_state_e +ico_uws_get_ready_state(struct ico_uws_context *context) +{ + ico_uws_detail detail; + + _DBG("ico_uws_get_ready_state called"); + if (context == NULL) { + detail._ico_uws_error.code = ICO_UWS_ERR_INVALID_PARAM; + _ERR("invalid param (%d)", detail._ico_uws_error.code); + exec_callback(context, ICO_UWS_EVT_ERROR, NULL, &detail, NULL); + return ICO_UWS_STATE_UNKNOWN; + } + + _DBG("return state: %d", context->state); + return context->state; +} + +/*--------------------------------------------------------------------------*/ +/** + * @brief ico_uws_set_event_cb + * Set the event callback function. + * + * @param[in] context ico_uws context + * @param[in] callback callback function + * @param[in] user_data user data + * @return result + * @retval ICO_UWS_ERR_NONE success + * @retval others error + */ +/*--------------------------------------------------------------------------*/ +ICO_API int +ico_uws_set_event_cb(struct ico_uws_context *context, + ico_uws_evt_cb callback, void *user_data) +{ + ico_uws_detail detail; + int i; + + _DBG("ico_uws_set_event_cb called"); + if (context == NULL || callback == NULL) { + /* invalid parameter */ + _ERR("invalid param"); + return ICO_UWS_ERR_INVALID_PARAM; + } + + /* set callback & user data */ + context->callback = callback; + context->user_data = user_data; + + /* call callback */ + for (i = 0; i < ICO_UWS_MAX_FDS; i++) { + if (context->callback_fd_list[i].fd != 0) { + detail._ico_uws_fd.fd = context->callback_fd_list[i].fd; + context->callback_fd_list[i].fd = 0; + exec_callback(context, ICO_UWS_EVT_ADD_FD, + context->callback_fd_list[i].wsi, &detail, NULL); + } + } + + return ICO_UWS_ERR_NONE; +} + +/*--------------------------------------------------------------------------*/ +/** + * @brief ico_uws_unset_event_cb + * Unset the event callback function. + * + * @param[in] context ico_uws context + * @return none + */ +/*--------------------------------------------------------------------------*/ +ICO_API void +ico_uws_unset_event_cb(struct ico_uws_context *context) +{ + _DBG("ico_uws_unset_event_cb called"); + + if (context == NULL) { + /* invalid parameter */ + _ERR("invalid param"); + return; + } + + /* unset callback & user data */ + context->callback = NULL; + context->user_data = NULL; + + return; +} diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 0000000..45f88b6 --- /dev/null +++ b/test/Makefile.am @@ -0,0 +1,37 @@ +TESTS_ENVIRONMENT = $(SHELL) $(top_srcdir)/test/run_test.sh + +export abs_builddir + +AM_CFLAGS = $(GCC_CFLAGS) +AM_CPPFLAGS = $(GCC_CFLAGS) + +noinst_PROGRAMS = \ + tst_ico_uws_client \ + tst_ico_uws_server \ + tst_ico_uws_multi_client \ + tst_ico_uws_multi_server + +check_LTLIBRARIES = $(TESTS) +check_PROGRAMS = tst_ico_uws_client tst_ico_uws_server tst_ico_uws_multi_client tst_ico_uws_multi_server + +test_common_lib = -lwebsockets +test_target_lib = ../src/.libs/libico-util-com.so + +tst_ico_uws_client_SOURCES = tst_ico_uws_client.c +tst_ico_uws_client_CFLAGS = -I../include $(OPT_CFLAGS) +tst_ico_uws_client_LDADD = $(test_target_lib) $(OPT_LIBS) $(test_common_lib) + +tst_ico_uws_server_SOURCES = tst_ico_uws_server.c +tst_ico_uws_server_CFLAGS = -I../include $(OPT_CFLAGS) +tst_ico_uws_server_LDADD = $(test_target_lib) $(OPT_LIBS) $(test_common_lib) + +tst_ico_uws_multi_client_SOURCES = tst_ico_uws_multi_client.c +tst_ico_uws_multi_client_CFLAGS = -I../include $(OPT_CFLAGS) +tst_ico_uws_multi_client_LDADD = $(test_target_lib) $(OPT_LIBS) $(test_common_lib) + +tst_ico_uws_multi_server_SOURCES = tst_ico_uws_multi_server.c +tst_ico_uws_multi_server_CFLAGS = -I../include $(OPT_CFLAGS) +tst_ico_uws_multi_server_LDADD = $(test_target_lib) $(OPT_LIBS) $(test_common_lib) + +EXTRA_DIST = run_test.sh + diff --git a/test/run_test.sh b/test/run_test.sh new file mode 100755 index 0000000..dd9c881 --- /dev/null +++ b/test/run_test.sh @@ -0,0 +1,331 @@ +#!/bin/sh + +######################## +# +# Setting value +# +######################## +# directory to put test's result in +rslt_dir="./result" +log_dir="${rslt_dir}/full_log" +# number of tests +num_tst_loop=1 + +# test log tag +tst_tag="TestCase" + +# log file name (common) +date_str=`date '+%Y%m%d'` +time_str=`date '+%H%M'` +file_str="${date_str}_${time_str}.txt" +srv_file_str="server_${file_str}" +clt_file_str="client_${file_str}" + +# set library path +export LD_LIBRARY_PATH=../src/.libs:$LD_LIBRARY_PATH + +######################## +# +# Make a directory to put test's result in +# +######################## +if [ ! -e ${rslt_dir} ]; then + mkdir ${rslt_dir} +fi +if [ ! -e ${log_dir} ]; then + mkdir ${log_dir} +fi + +######################## +# +# Set the number of the test's loop +# (if argument exists) +# +######################## +if [ $# -ne 0 ]; then + if expr "$1" : '[0-9]*' > /dev/null ; then + num_tst_loop=$1 + fi +fi + +######################## +# +# Function +# +######################## +kill_old_proc() +{ + pids=(`ps -ef | grep tst_ico_uws | grep -v grep | awk '{ print $2 }'`) + for pid in ${pids[*]} + do + kill -9 ${pid} + done +} + +check_srv_no_exist() +{ + while : + do + proc_srv=`pgrep -lf "$1"` + if [ -n "${proc_srv}" ]; then + break + fi + # sleep while process of server does not exist + usleep 100000 + done +} + +check_srv_exist() +{ + while : + do + proc_srv=`pgrep -lf "$1"` + if [ -z "${proc_srv}" ]; then + break + fi + # sleep while process of server exists + sleep 1 + done +} + +print_result() +{ + local l_type="$1" + local l_log="$2" + local l_log_total="$3" + local l_cnt_ok=0 + local l_cnt_ng=0 + local l_str="" + + # title + echo "" | tee -a ${l_log_total} + echo "----- ${l_type} result -----" | tee -a ${l_log_total} + # count OK/NG, and output console and file + l_cnt_ok=`grep ${tst_tag} ${l_log} | grep "OK" | wc -l` + l_cnt_ng=`grep ${tst_tag} ${l_log} | grep "NG" | wc -l` + l_str="<<Results Total>> OK: ${l_cnt_ok}, NG: ${l_cnt_ng}" + l_str="${l_str} (num of tests: ${num_tst_loop})" + echo "${l_str}" | tee -a ${l_log_total} + # grep test result, and output to file + grep ${tst_tag} ${l_log} | tee -a ${l_log_total} +} + +exec_test() +{ + local l_tst_no="0$3" + local l_app_srv="./$1" + local l_app_clt="./$2" + + local l_log_srv="${log_dir}/tst${l_tst_no}_${srv_file_str}" + local l_log_clt="${log_dir}/tst${l_tst_no}_${clt_file_str}" + local l_log_total="${rslt_dir}/tst${l_tst_no}_${file_str}" + + # kill old process if exists + kill_old_proc + + sleep 1 + + for i in `seq 1 ${num_tst_loop}` + do + # execute server + ${l_app_srv} >> ${l_log_srv} & + # sleep while process of server does not exist + check_srv_no_exist ${l_app_srv} + # execute client + ${l_app_clt} >> ${l_log_clt} + + # sleep while process of server exists + check_srv_exist ${l_app_srv} + done + + print_result "Server" ${l_log_srv} ${l_log_total} + print_result "Client" ${l_log_clt} ${l_log_total} + sleep 1 +} + +exec_test_multi_clt() +{ + local l_tst_no="0$4" + local l_app_srv="./$1" + local l_app_clt="./$2" + local l_app_clt_sec="./$3" + + local l_log_srv="${log_dir}/tst${l_tst_no}_${srv_file_str}" + local l_log_clt="${log_dir}/tst${l_tst_no}_client0_${file_str}" + local l_log_clt_sec="${log_dir}/tst${l_tst_no}_client1_${file_str}" + local l_log_total="${rslt_dir}/tst${l_tst_no}_${file_str}" + + # kill old process if exists + kill_old_proc + + sleep 1 + + for i in `seq 1 ${num_tst_loop}` + do + # execute server + ${l_app_srv} >> ${l_log_srv} & + # sleep while process of server does not exist + check_srv_no_exist ${l_app_srv} + # execute client + ${l_app_clt} >> ${l_log_clt} & + usleep 100 + ${l_app_clt_sec} >> ${l_log_clt_sec} + + # sleep while process of server exists + check_srv_exist ${l_app_srv} + done + + print_result "Server" ${l_log_srv} ${l_log_total} + print_result "Client 0" ${l_log_clt} ${l_log_total} + print_result "Client 1" ${l_log_clt_sec} ${l_log_total} + sleep 1 +} + +exec_test_multi_srv() +{ + local l_tst_no="0$4" + local l_app_srv="./$1" + local l_app_srv_sec="./$2" + local l_app_clt="./$3" + + local l_log_srv="${log_dir}/tst${l_tst_no}_server0_${file_str}" + local l_log_srv_sec="${log_dir}/tst${l_tst_no}_server0_${file_str}" + local l_log_clt="${log_dir}/tst${l_tst_no}_${clt_file_str}" + local l_log_total="${rslt_dir}/tst${l_tst_no}_${file_str}" + + # kill old process if exists + kill_old_proc + + sleep 1 + + for i in `seq 1 ${num_tst_loop}` + do + # execute server + ${l_app_srv} >> ${l_log_srv} & + usleep 500 + ${l_app_srv_sec} >> ${l_log_srv_sec} & + # sleep while process of server does not exist + check_srv_no_exist ${l_app_srv} + check_srv_no_exist ${l_app_srv_sec} + # execute client + ${l_app_clt} >> ${l_log_clt} + + # sleep while process of server exists + check_srv_exist ${l_app_srv} + check_srv_exist ${l_app_srv_sec} + done + + print_result "Server 0" ${l_log_srv} ${l_log_total} + print_result "Server 1" ${l_log_srv_sec} ${l_log_total} + print_result "Client" ${l_log_clt} ${l_log_total} + sleep 1 +} + +######################## +# +# Test Start +# +######################## +echo "" +echo "=== API Test Start ===" + +######################## +# +# API Test (1) +# 1 server / 1 client +# +######################## +# application +app_srv="tst_ico_uws_server -p 8080" +app_clt="tst_ico_uws_client -p 8080" + +# test & output result +echo "" +tst_no=1 +echo "=== API Test ($tst_no) <<1 server, 1 client>> Start ===" +exec_test "${app_srv}" "${app_clt}" ${tst_no} +echo "=== API Test ($tst_no) <<1 server, 1 client>> End ===" + + +######################## +# +# API Test (2) +# 1 server / 2 client +# +######################## +# application +app_srv="tst_ico_uws_server -p 8080" +app_clt="tst_ico_uws_client -p 8080" + +# test & output result +echo "" +tst_no=2 +echo "=== API Test ($tst_no) <<1 server, 2 client>> Start ===" +exec_test_multi_clt "${app_srv}" "${app_clt}" "${app_clt}" ${tst_no} +echo "=== API Test ($tst_no) <<1 server, 2 client>> End ===" + + +######################## +# +# API Test (3) +# 2 server / 1 client (2 thread) +# +######################## +# application +app_srv="tst_ico_uws_server -p 8080" +app_srv_sec="tst_ico_uws_server -p 9090" +app_clt="tst_ico_uws_multi_client" + +# test & output result +echo "" +tst_no=3 +echo "=== API Test ($tst_no) <<2 server, 1 client>> Start ===" +exec_test_multi_srv "${app_srv}" "${app_srv_sec}" "${app_clt}" ${tst_no} +echo "=== API Test ($tst_no) <<2 server, 1 client>> End ===" + + +######################## +# +# API Test (4) +# 1 server (2 thread) / 1 client (2 thread) +# +######################## +# application +app_srv="tst_ico_uws_multi_server" +app_clt="tst_ico_uws_multi_client" + +# test & output result +echo "" +tst_no=4 +echo "=== API Test ($tst_no) <<multi server, multi client>> Start ===" +exec_test "${app_srv}" "${app_clt}" ${tst_no} +echo "=== API Test ($tst_no) <<multi server, multi client>> End ===" + + +######################## +# +# API Test (5) +# 1 server (2 thread) / 2 client (2 process) +# +######################## +# application +app_srv="tst_ico_uws_multi_server" +app_clt="tst_ico_uws_client -p 8080" +app_clt_sec="tst_ico_uws_client -p 9090" + +# test & output result +echo "" +tst_no=5 +echo "=== API Test ($tst_no) <<multi server, 2 client>> Start ===" +exec_test_multi_clt "${app_srv}" "${app_clt}" "${app_clt_sec}" ${tst_no} +echo "=== API Test ($tst_no) <<multi server, 2 client>> End ===" + + +######################## +# +# Test End +# +######################## +echo "=== API Test End ===" +echo "" + diff --git a/test/tst_ico_uws.h b/test/tst_ico_uws.h new file mode 100644 index 0000000..9c33f05 --- /dev/null +++ b/test/tst_ico_uws.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013, TOYOTA MOTOR CORPORATION. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#ifndef __TST_ICO_UWS_H__ +#define __TST_ICO_UWS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define dbg_print(fmt, ...) \ + printf("[TestCase] " fmt, __VA_ARGS__); + +#define TEST_OK "OK" +#define TEST_NG "NG" + +#define SET_FLAG 1 +#define UNSET_FLAG 0 + +#define SRV_URI "ws://127.0.0.1" +#define SRV_PORT "8080" +#define PROTOCOL_NAME "test_protocol" + +#define CLT_DATA "test data from client" + +#define MAX_DATA_NUM 2 + +char *srv_ports[MAX_DATA_NUM] = {SRV_PORT, "9090"}; +char *clt_datas[MAX_DATA_NUM] = {CLT_DATA, "test to send data"}; + +#ifdef __cplusplus +} +#endif + +#endif /* __TST_ICO_UWS_H__ */ diff --git a/test/tst_ico_uws_client.c b/test/tst_ico_uws_client.c new file mode 100644 index 0000000..7647e9c --- /dev/null +++ b/test/tst_ico_uws_client.c @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2013, TOYOTA MOTOR CORPORATION. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ +/** + * @brief test client (socket library for communicate) + * + * @date June-7-2013 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <glib.h> +#include "ico_uws.h" + +#include "tst_ico_uws.h" + +/* ----------------------------------------------- */ +/* Variable */ +/* ----------------------------------------------- */ +#define SLEEP_TIME 2 + +/* context */ +static struct ico_uws_context *clt_context; +static void *clt_id; + +/* receive event check */ +static int receive_flag = UNSET_FLAG; + +/* callback function is setting or not setting */ +static int set_cb_flag = UNSET_FLAG; +static int num_call_cb = 0; + +/* ----------------------------------------------- */ +/* Define of static function */ +/* ----------------------------------------------- */ +static void tst_uws_callback(const struct ico_uws_context *context, + const ico_uws_evt_e event, + const void *id, + const ico_uws_detail *detail, + void *user_data); +static void tst_create_context(char *uri); +static void tst_get_ready_state(int state, char *str_state); +static void tst_get_uri(char *set_uri); +static void tst_send(unsigned char *data); +static void tst_service(void); +static void tst_close(void); +static void tst_set_evt_callback(unsigned char *send_data); +static void tst_unset_evt_callback(void); +static int ico_uws_client_test(char *uri, unsigned char *data); + +/* ----------------------------------------------- */ +/* Public API Test */ +/* ----------------------------------------------- */ +/* event callback */ +static void +tst_uws_callback(const struct ico_uws_context *context, + const ico_uws_evt_e event, + const void *id, + const ico_uws_detail *detail, + void *user_data) +{ + char str[256]; + char *ret_str; + + num_call_cb++; + if (set_cb_flag == SET_FLAG) { + ret_str = TEST_OK; + } + else { + ret_str = TEST_NG; + } + + /* set id */ + clt_id = (void *)id; + + switch (event) { + case ICO_UWS_EVT_OPEN: + sprintf(str, "open"); + if (clt_context != NULL) { + tst_get_ready_state(ICO_UWS_STATE_OPEN, "open"); + } + break; + case ICO_UWS_EVT_ERROR: + sprintf(str, "error"); + if (detail->_ico_uws_error.code == ICO_UWS_ERR_SEND) { + dbg_print("ico_uws_service (client) : %s\n", TEST_NG); + dbg_print("ico_uws_send (client) : %s\n", TEST_NG); + receive_flag = SET_FLAG; + } + break; + case ICO_UWS_EVT_CLOSE: + sprintf(str, "close"); + if (clt_context != NULL) { + tst_get_ready_state(ICO_UWS_STATE_CLOSED, "close"); + } + break; + case ICO_UWS_EVT_RECEIVE: + sprintf(str, "receive"); + char *data = (char *)detail->_ico_uws_message.recv_data; + if (strcmp((char *)user_data, data) != 0) { + dbg_print("ico_uws_send (client) : %s\n", TEST_NG); + } else { + dbg_print("ico_uws_send (client) : %s\n", TEST_OK); + } + sprintf(str, "%s '%s'", str, data); + receive_flag = SET_FLAG; + break; + case ICO_UWS_EVT_ADD_FD: + sprintf(str, "add fd(%d)", detail->_ico_uws_fd.fd); + break; + case ICO_UWS_EVT_DEL_FD: + sprintf(str, "delete fd(%d)", detail->_ico_uws_fd.fd); + break; + default: + /* other event is not test */ + break; + } + dbg_print("ico_uws_evt_cb [%d (%s)] (client) : %s\n", + event, str, ret_str); + + return; +} + +/* create context */ +static void +tst_create_context(char *uri) +{ + char *ret_str = TEST_OK; + + clt_context = ico_uws_create_context(uri, PROTOCOL_NAME); + if (clt_context == NULL) { + ret_str = TEST_NG; + } + dbg_print("ico_uws_create_context (client) : %s\n", ret_str); + + return; +} + +/* get ready state */ +static void +tst_get_ready_state(int state, char *str_state) +{ + char *ret_str = TEST_OK; + + ico_uws_state_e cur_state = ico_uws_get_ready_state(clt_context); + if (cur_state != state) { + ret_str = TEST_NG; + } + dbg_print("ico_uws_get_ready_state [%s] (client) : %s\n", + str_state, ret_str); + + return; +} + +/* get uri */ +static void +tst_get_uri(char *set_uri) +{ + char *ret_str = TEST_OK; + + char *uri = ico_uws_get_uri(clt_context); + if (strcmp(uri, set_uri) != 0) { + ret_str = TEST_NG; + } + dbg_print("ico_uws_get_uri [%s] (client) : %s\n", + uri, ret_str); + + return; +} + +/* send data */ +static void +tst_send(unsigned char *data) +{ + int i; + size_t len = strlen((char *)data) + 1; + + for (i = 0; i < 10; i++) { + ico_uws_service(clt_context); + usleep(100); + } + ico_uws_send(clt_context, clt_id, data, len); + + return; +} + +/* service loop (wait to receive data) */ +static void +tst_service() +{ + char *ret_str = TEST_OK; + + /* wait to close the connection */ + while (receive_flag == UNSET_FLAG) { + ico_uws_service(clt_context); + usleep(50); + } + receive_flag = UNSET_FLAG; + dbg_print("ico_uws_service (client) : %s\n", ret_str); + + return; +} + +/* close */ +static void +tst_close() +{ + char *ret_str = TEST_OK; + + ico_uws_close(clt_context); + dbg_print("ico_uws_close (client) : %s\n", ret_str); + + return; +} + +/* set callback */ +static void +tst_set_evt_callback(unsigned char *send_data) +{ + int ret; + char *ret_str = TEST_OK; + + /* set callback */ + set_cb_flag = SET_FLAG; + ret = ico_uws_set_event_cb(clt_context, tst_uws_callback, + (void *)send_data); + if (ret != ICO_UWS_ERR_NONE) { + ret_str = TEST_NG; + dbg_print("ico_uws_set_event_cb (client) : %s (%d)\n", + ret_str, ret); + return; + } + + dbg_print("ico_uws_set_event_cb (client) : %s\n", ret_str); + + return; +} + +/* unset callback */ +static void +tst_unset_evt_callback() +{ + char *ret_str = TEST_OK; + + /* unset callback */ + ico_uws_unset_event_cb(clt_context); + set_cb_flag = UNSET_FLAG; + num_call_cb = 0; + + /* occurs the error event */ + (void)ico_uws_get_uri(NULL); + sleep(SLEEP_TIME); + if (num_call_cb > 0) { + ret_str = TEST_NG; + } + + dbg_print("ico_uws_unset_event_cb (client) : %s\n", ret_str); + + return; +} + +/* test main (to connect to single server) */ +static int +ico_uws_client_test(char *uri, unsigned char *data) +{ + /* create context */ + tst_create_context(uri); + + /* set callback */ + tst_set_evt_callback(data); + + /* interval */ + sleep(SLEEP_TIME); + + if (clt_context) { + /* get uri */ + tst_get_uri(uri); + + /* send data */ + tst_send(data); + + /* wait to receive data */ + tst_service(); + + /* interval */ + sleep(SLEEP_TIME); + + /* unset callback */ + tst_unset_evt_callback(); + + /* close */ + tst_close(); + } + + return 1; +} + +/* ----------------------------------------------- */ +/* Main */ +/* ----------------------------------------------- */ +static GMainLoop *g_mainloop = NULL; + +static gboolean +exit_program(gpointer data) +{ + g_main_loop_quit(g_mainloop); + + return FALSE; +} + +/* main */ +int +main(int argc, char **argv) +{ + char uri[128]; + unsigned char data[256]; + int id; + int set_uri_flag = UNSET_FLAG; + int set_data_flag = UNSET_FLAG; + + for (id = 0; id < argc; id++) { + if (strcmp(argv[id], "-p") == 0) { + /* set uri to connect */ + id++; + sprintf(uri, "%s:%s", SRV_URI, argv[id]); + set_uri_flag = SET_FLAG; + } + else if (strcmp(argv[id], "-d") == 0) { + /* set data to send */ + id++; + sprintf((char *)data, "%s", argv[id]); + data[strlen(argv[id]) + 1] = '\0'; + set_data_flag = SET_FLAG; + } + } + + /* set default uri to connect */ + if (set_uri_flag == UNSET_FLAG) { + sprintf(uri, "%s:%s", SRV_URI, SRV_PORT); + } + + /* set default data to send */ + if (set_data_flag == UNSET_FLAG) { + sprintf((char *)data, "%s", CLT_DATA); + } + + g_setenv("PKG_NAME", "org.tizen.ico.tst_ico_uws_client", 1); + g_mainloop = g_main_loop_new(NULL, 0); + + printf("\n"); + printf("##### ico_uws API (client) Test Start #####\n"); + ico_uws_client_test(uri, data); + printf("##### ico_uws API (client) Test End #####\n"); + printf("\n"); + + g_timeout_add_seconds(2, exit_program, NULL); + g_main_loop_run(g_mainloop); + + return 0; +} diff --git a/test/tst_ico_uws_multi_client.c b/test/tst_ico_uws_multi_client.c new file mode 100644 index 0000000..06fab7d --- /dev/null +++ b/test/tst_ico_uws_multi_client.c @@ -0,0 +1,548 @@ +/* + * Copyright (c) 2013, TOYOTA MOTOR CORPORATION. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ +/** + * @brief test client to connect to multi servers + * (socket library for communicate) + * + * @date June-27-2013 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> +#include <unistd.h> + +#include <glib.h> +#include "ico_uws.h" + +#include "tst_ico_uws.h" + +/* ----------------------------------------------- */ +/* Variable */ +/* ----------------------------------------------- */ +#define SLEEP_TIME 3 + +struct tst_client_t{ + struct tst_client_t *next; + struct ico_uws_context *context; + char uri[128]; + unsigned char data[256]; + size_t len; + void *clt_id; /* use to send data */ + int receive_flag; + int open_flag; + int set_cb_flag; + int num_call_cb; + int id; +}; + +struct tst_client_t *first_clt = NULL; +struct tst_client_t *second_clt = NULL; + +/* pthread mutex initialize */ +static pthread_mutex_t multi_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* ----------------------------------------------- */ +/* Define of static function */ +/* ----------------------------------------------- */ +static void tst_uws_callback(const struct ico_uws_context *context, + const ico_uws_evt_e event, + const void *id, + const ico_uws_detail *detail, + void *user_data); +static void tst_create_context(struct tst_client_t *clt_t); +static void tst_get_ready_state(struct tst_client_t *clt_t, + int state, char *str_state); +static void tst_get_uri(struct tst_client_t *clt_t); +static void tst_send(struct tst_client_t *clt_t); +static void tst_service_open(struct tst_client_t *clt_t); +static void tst_service_receive(struct tst_client_t *clt_t); +static void tst_close(struct tst_client_t *clt_t); +static void tst_set_evt_callback(struct tst_client_t *clt_t); +static void tst_unset_evt_callback(struct tst_client_t *clt_t); +static struct tst_client_t *ico_uws_client_test_init(int id); +static void *tst_client_thread(void *args); +static void *tst_client_thread_sec(void *args); +static int ico_uws_client_test_multi(void); + +/* ----------------------------------------------- */ +/* Public API Test */ +/* ----------------------------------------------- */ +/* event callback */ +static void +tst_uws_callback(const struct ico_uws_context *context, + const ico_uws_evt_e event, + const void *id, + const ico_uws_detail *detail, + void *user_data) +{ + char str[256]; + char *ret_str; + struct tst_client_t *clt_t; + + if (context == NULL) return; + + if (first_clt != NULL && context == first_clt->context) { + clt_t = first_clt; + } + else if (second_clt != NULL && context == second_clt->context) { + clt_t = second_clt; + } + else { + return; + } + + clt_t->num_call_cb++; + if (clt_t->set_cb_flag == SET_FLAG) { + ret_str = TEST_OK; + } + else { + ret_str = TEST_NG; + } + + switch (event) { + case ICO_UWS_EVT_OPEN: + sprintf(str, "open"); + clt_t->clt_id = (void *)id; + clt_t->open_flag = SET_FLAG; + break; + case ICO_UWS_EVT_ERROR: + sprintf(str, "error"); + if (detail->_ico_uws_error.code == ICO_UWS_ERR_SEND) { + dbg_print("ico_uws_service (client %d) : %s\n", + clt_t->id, TEST_NG); + dbg_print("ico_uws_send (client %d) : %s\n", + clt_t->id, TEST_NG); + clt_t->receive_flag = SET_FLAG; + } + break; + case ICO_UWS_EVT_CLOSE: + sprintf(str, "close"); + tst_get_ready_state(clt_t, ICO_UWS_STATE_CLOSED, "close"); + break; + case ICO_UWS_EVT_RECEIVE: + sprintf(str, "receive"); + char *data = (char *)detail->_ico_uws_message.recv_data; + char *send_data = (char *)clt_t->data; + if (strcmp(send_data, data) != 0) { + dbg_print("ico_uws_send (client %d) : %s\n", + clt_t->id, TEST_NG); + } else { + dbg_print("ico_uws_send (client %d) : %s\n", + clt_t->id, TEST_OK); + } + clt_t->receive_flag = SET_FLAG; + sprintf(str, "%s '%s'", str, data); + break; + case ICO_UWS_EVT_ADD_FD: + sprintf(str, "add fd(%d)", detail->_ico_uws_fd.fd); + break; + case ICO_UWS_EVT_DEL_FD: + sprintf(str, "delete fd(%d)", detail->_ico_uws_fd.fd); + break; + default: + /* other event is not test */ + break; + } + + printf("@@@ ico_uws_evt_cb [%d (%s)] (client %d) : %s\n", + event, str, clt_t->id, ret_str); + + return; +} + +/* create context */ +static void +tst_create_context(struct tst_client_t *clt_t) +{ + char *ret_str = TEST_OK; + + /* mutex lock */ + pthread_mutex_lock(&multi_mutex); + clt_t->context = ico_uws_create_context(clt_t->uri, PROTOCOL_NAME); + /* mutex unlock */ + pthread_mutex_unlock(&multi_mutex); + if (clt_t->context == NULL) { + ret_str = TEST_NG; + } + dbg_print("ico_uws_create_context (client %d) : %s\n", + clt_t->id, ret_str); + + return; +} + +/* get ready state */ +static void +tst_get_ready_state(struct tst_client_t *clt_t, + int state, char *str_state) +{ + char *ret_str = TEST_OK; + + /* mutex lock */ + pthread_mutex_lock(&multi_mutex); + ico_uws_state_e cur_state = ico_uws_get_ready_state(clt_t->context); + /* mutex unlock */ + pthread_mutex_unlock(&multi_mutex); + if (cur_state != state) { + ret_str = TEST_NG; + } + dbg_print("ico_uws_get_ready_state [%s] (client %d) : %s\n", + str_state, clt_t->id, ret_str); + + return; +} + +/* get uri */ +static void +tst_get_uri(struct tst_client_t *clt_t) +{ + char *ret_str = TEST_OK; + + /* mutex lock */ + pthread_mutex_lock(&multi_mutex); + char *uri = ico_uws_get_uri(clt_t->context); + /* mutex unlock */ + pthread_mutex_unlock(&multi_mutex); + if (strcmp(uri, clt_t->uri) != 0) { + ret_str = TEST_NG; + } + dbg_print("ico_uws_get_uri [%s] (client %d) : %s\n", + uri, clt_t->id, ret_str); + + return; +} + +/* send data */ +static void +tst_send(struct tst_client_t *clt_t) +{ + int i; + + for (i = 0; i < 10; i++) { + /* mutex lock */ + pthread_mutex_lock(&multi_mutex); + ico_uws_service(clt_t->context); + /* mutex unlock */ + pthread_mutex_unlock(&multi_mutex); + usleep(100); + } + + /* mutex lock */ + pthread_mutex_lock(&multi_mutex); + ico_uws_send(clt_t->context, clt_t->clt_id, clt_t->data, clt_t->len); + /* mutex unlock */ + pthread_mutex_unlock(&multi_mutex); + + return; +} + +/* service loop (wait to open) */ +static void +tst_service_open(struct tst_client_t *clt_t) +{ + char *ret_str = TEST_OK; + + while (clt_t->open_flag == UNSET_FLAG) { + /* mutex lock */ + pthread_mutex_lock(&multi_mutex); + ico_uws_service(clt_t->context); + /* mutex unlock */ + pthread_mutex_unlock(&multi_mutex); + usleep(50); + } + clt_t->open_flag = UNSET_FLAG; + dbg_print("ico_uws_service (client %d open) : %s\n", clt_t->id, ret_str); + + return; +} + +/* service loop (wait to receive data) */ +static void +tst_service_receive(struct tst_client_t *clt_t) +{ + char *ret_str = TEST_OK; + + while (clt_t->receive_flag == UNSET_FLAG) { + /* mutex lock */ + pthread_mutex_lock(&multi_mutex); + ico_uws_service(clt_t->context); + /* mutex unlock */ + pthread_mutex_unlock(&multi_mutex); + usleep(50); + } + clt_t->receive_flag = UNSET_FLAG; + dbg_print("ico_uws_service (client %d receive) : %s\n", clt_t->id, ret_str); + + return; +} + +/* close */ +static void +tst_close(struct tst_client_t *clt_t) +{ + char *ret_str = TEST_OK; + + /* mutex lock */ + pthread_mutex_lock(&multi_mutex); + ico_uws_close(clt_t->context); + /* mutex unlock */ + pthread_mutex_unlock(&multi_mutex); + + dbg_print("ico_uws_close (client %d) : %s\n", clt_t->id, ret_str); + + return; +} + +/* set callback */ +static void +tst_set_evt_callback(struct tst_client_t *clt_t) +{ + int ret; + char *ret_str = TEST_OK; + + clt_t->set_cb_flag = SET_FLAG; + /* mutex lock */ + pthread_mutex_lock(&multi_mutex); + /* set callback */ + ret = ico_uws_set_event_cb(clt_t->context, tst_uws_callback, NULL); + /* mutex unlock */ + pthread_mutex_unlock(&multi_mutex); + if (ret != ICO_UWS_ERR_NONE) { + ret_str = TEST_NG; + dbg_print("ico_uws_set_event_cb (client %d) : %s (%d)\n", + clt_t->id, ret_str, ret); + return; + } + + dbg_print("ico_uws_set_event_cb (client %d) : %s\n", + clt_t->id, ret_str); + + return; +} + +/* unset callback */ +static void +tst_unset_evt_callback(struct tst_client_t *clt_t) +{ + char *ret_str = TEST_OK; + + /* mutex lock */ + pthread_mutex_lock(&multi_mutex); + /* unset callback */ + ico_uws_unset_event_cb(clt_t->context); + /* mutex unlock */ + pthread_mutex_unlock(&multi_mutex); + + clt_t->set_cb_flag = UNSET_FLAG; + clt_t->num_call_cb = 0; + + /* mutex lock */ + pthread_mutex_lock(&multi_mutex); + /* occurs the error event */ + (void)ico_uws_get_uri(NULL); + /* mutex unlock */ + pthread_mutex_unlock(&multi_mutex); + sleep(SLEEP_TIME); + if (clt_t->num_call_cb > 0) { + ret_str = TEST_NG; + } + + dbg_print("ico_uws_unset_event_cb (client %d) : %s\n", + clt_t->id, ret_str); + + return; +} + +/* prepare for test */ +static struct tst_client_t * +ico_uws_client_test_init(int id) +{ + struct tst_client_t *clt_t; + + clt_t = calloc(1, sizeof(struct tst_client_t)); + if (clt_t == NULL) { + printf("calloc failed\n"); + return NULL; + } + + /* set uri to connect to */ + sprintf(clt_t->uri, "%s:%s", SRV_URI, srv_ports[id]); + /* set data to send */ + sprintf((char *)clt_t->data, "%s", clt_datas[id]); + clt_t->len = strlen(clt_datas[id]) + 1; + clt_t->data[clt_t->len] = '\0'; + + /* initialize */ + clt_t->context = NULL; + clt_t->clt_id = NULL; + clt_t->receive_flag = UNSET_FLAG; + clt_t->open_flag = UNSET_FLAG; + clt_t->set_cb_flag = UNSET_FLAG; + clt_t->num_call_cb = 0; + clt_t->id = id; + + return clt_t; +} + +/* ----------------------------------------------- */ +/* Test Main */ +/* ----------------------------------------------- */ +static void * +tst_client_thread(void *args) +{ + /* prepare for test */ + first_clt = ico_uws_client_test_init(0); + if (first_clt == NULL) { + return NULL; + } + + /* create context */ + tst_create_context(first_clt); + + if (first_clt->context != NULL) { + /* set callback */ + tst_set_evt_callback(first_clt); + + /* wait to open */ + tst_service_open(first_clt); + + /* check the state */ + tst_get_ready_state(first_clt, ICO_UWS_STATE_OPEN, "open"); + + /* check the uri */ + tst_get_uri(first_clt); + + /* send data */ + tst_send(first_clt); + + /* wait to receive data */ + tst_service_receive(first_clt); + + /* interval */ + sleep(SLEEP_TIME); + + /* unset callback */ + tst_unset_evt_callback(first_clt); + + /* interval */ + sleep(SLEEP_TIME); + + /* session close */ + tst_close(first_clt); + } + /* free memory */ + free(first_clt); + + return NULL; +} + +static void * +tst_client_thread_sec(void *args) +{ + /* prepare for test */ + second_clt = ico_uws_client_test_init(1); + if (second_clt == NULL) { + return NULL; + } + + /* create context */ + tst_create_context(second_clt); + + if (second_clt->context != NULL) { + /* set callback */ + tst_set_evt_callback(second_clt); + + /* wait to open */ + tst_service_open(second_clt); + + /* check the state */ + tst_get_ready_state(second_clt, ICO_UWS_STATE_OPEN, "open"); + + /* check the uri */ + tst_get_uri(second_clt); + + /* send data */ + tst_send(second_clt); + + /* wait to receive data */ + tst_service_receive(second_clt); + + /* interval */ + sleep(SLEEP_TIME); + + /* unset callback */ + tst_unset_evt_callback(second_clt); + + /* interval */ + sleep(SLEEP_TIME); + + /* session close */ + tst_close(second_clt); + } + + /* free memory */ + free(second_clt); + + return NULL; +} + +/* test main (to connect to multi servers) */ +static int +ico_uws_client_test_multi() +{ + pthread_t thread, thread_sec; + + /* client to connect server (port: 8080) */ + pthread_create( &thread, NULL, tst_client_thread, (void *)NULL ); + /* client to connect server (port: 9090) */ + pthread_create( &thread_sec, NULL, tst_client_thread_sec, (void *)NULL ); + + pthread_join( thread, NULL ); + pthread_join( thread_sec, NULL ); + + /* interval */ + sleep(SLEEP_TIME); + + return 1; +} + +/* ----------------------------------------------- */ +/* Main */ +/* ----------------------------------------------- */ +static GMainLoop *g_mainloop = NULL; + +static gboolean +exit_program(gpointer data) +{ + g_main_loop_quit(g_mainloop); + + return FALSE; +} + +/* main */ +int +main(int argc, char **argv) +{ + g_setenv("PKG_NAME", "org.tizen.ico.tst_ico_uws_mlt_client", 1); + g_mainloop = g_main_loop_new(NULL, 0); + + printf("\n"); + printf("##### ico_uws API (client to connect to multi servers)"); + printf(" Test Start #####\n"); + ico_uws_client_test_multi(); + printf("##### ico_uws API (client to connect to multi servers)"); + printf(" Test End #####\n"); + printf("\n"); + + g_timeout_add_seconds(2, exit_program, NULL); + g_main_loop_run(g_mainloop); + + return 0; +} diff --git a/test/tst_ico_uws_multi_server.c b/test/tst_ico_uws_multi_server.c new file mode 100644 index 0000000..ba928d1 --- /dev/null +++ b/test/tst_ico_uws_multi_server.c @@ -0,0 +1,541 @@ +/* + * Copyright (c) 2013, TOYOTA MOTOR CORPORATION. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ +/** + * @brief test server to listen to multi servers + * (socket library for communicate) + * + * @date June-27-2013 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> +#include <unistd.h> + +#include <glib.h> +#include "ico_uws.h" + +#include "tst_ico_uws.h" + +/* ----------------------------------------------- */ +/* Variable */ +/* ----------------------------------------------- */ +#define SLEEP_TIME 3 + +struct tst_server_t{ + struct tst_server_t *next; + struct ico_uws_context *context; + char uri[128]; + unsigned char data[256]; + size_t len; + void *srv_id; /* use to send data */ + int receive_flag; + int close_flag; + int set_cb_flag; + int num_call_cb; + int id; +}; + +struct tst_server_t *first_srv = NULL; +struct tst_server_t *second_srv = NULL; + +/* pthread mutex initialize */ +static pthread_mutex_t multi_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* ----------------------------------------------- */ +/* Define of static function */ +/* ----------------------------------------------- */ +static void tst_uws_callback(const struct ico_uws_context *context, + const ico_uws_evt_e event, + const void *id, + const ico_uws_detail *detail, + void *user_data); +static void tst_create_context(struct tst_server_t *srv_t); +static void tst_get_ready_state(struct tst_server_t *srv_t, + int state, char *str_state); +static void tst_get_uri(struct tst_server_t *srv_t); +static void tst_send(struct tst_server_t *srv_t); +static void tst_service_receive(struct tst_server_t *srv_t); +static void tst_service_close(struct tst_server_t *srv_t); +static void tst_close(struct tst_server_t *srv_t); +static void tst_set_evt_callback(struct tst_server_t *srv_t); +static void tst_unset_evt_callback(struct tst_server_t *srv_t); +static struct tst_server_t *ico_uws_server_test_init(int id); +static void *tst_server_thread(void *args); +static void *tst_server_thread_sec(void *args); +static int ico_uws_server_test_multi(void); + +/* ----------------------------------------------- */ +/* Public API Test */ +/* ----------------------------------------------- */ +/* event callback */ +static void +tst_uws_callback(const struct ico_uws_context *context, + const ico_uws_evt_e event, + const void *id, + const ico_uws_detail *detail, + void *user_data) +{ + char str[256]; + char *ret_str; + struct tst_server_t *srv_t; + + if (context == NULL) return; + + if (first_srv != NULL && context == first_srv->context) { + srv_t = first_srv; + } + else if (second_srv != NULL && context == second_srv->context) { + srv_t = second_srv; + } + else { + return; + } + + srv_t->num_call_cb++; + if (srv_t->set_cb_flag == SET_FLAG) { + ret_str = TEST_OK; + } + else { + ret_str = TEST_NG; + } + + switch (event) { + case ICO_UWS_EVT_OPEN: + sprintf(str, "open"); + break; + case ICO_UWS_EVT_ERROR: + sprintf(str, "error"); + break; + case ICO_UWS_EVT_CLOSE: + sprintf(str, "close"); + srv_t->close_flag = SET_FLAG; + break; + case ICO_UWS_EVT_RECEIVE: + sprintf(str, "receive"); + /* set id */ + srv_t->srv_id = (void *)id; + char *data = (char *)detail->_ico_uws_message.recv_data; + size_t len = detail->_ico_uws_message.recv_len; + /* set data to send */ + sprintf((char *)srv_t->data, "%s", data); + srv_t->len = len; + srv_t->receive_flag = SET_FLAG; + sprintf(str, "%s '%s'", str, data); + break; + case ICO_UWS_EVT_ADD_FD: + sprintf(str, "add fd(%d)", detail->_ico_uws_fd.fd); + break; + case ICO_UWS_EVT_DEL_FD: + sprintf(str, "delete fd(%d)", detail->_ico_uws_fd.fd); + break; + default: + /* other event is not test */ + break; + } + + printf("@@@ ico_uws_evt_cb [%d (%s)] (server %d) : %s\n", + event, str, srv_t->id, ret_str); + + return; +} + +/* create context */ +static void +tst_create_context(struct tst_server_t *srv_t) +{ + char *ret_str = TEST_OK; + + /* mutex lock */ + pthread_mutex_lock(&multi_mutex); + srv_t->context = ico_uws_create_context(srv_t->uri, PROTOCOL_NAME); + /* mutex unlock */ + pthread_mutex_unlock(&multi_mutex); + if (srv_t->context == NULL) { + ret_str = TEST_NG; + } + dbg_print("ico_uws_create_context (server %d) : %s\n", + srv_t->id, ret_str); + + return; +} + +/* get ready state */ +static void +tst_get_ready_state(struct tst_server_t *srv_t, + int state, char *str_state) +{ + char *ret_str = TEST_OK; + + /* mutex lock */ + pthread_mutex_lock(&multi_mutex); + ico_uws_state_e cur_state = ico_uws_get_ready_state(srv_t->context); + /* mutex unlock */ + pthread_mutex_unlock(&multi_mutex); + if (cur_state != state) { + ret_str = TEST_NG; + } + dbg_print("ico_uws_get_ready_state [%s] (server %d) : %s\n", + str_state, srv_t->id, ret_str); + + return; +} + +/* get uri */ +static void +tst_get_uri(struct tst_server_t *srv_t) +{ + char *ret_str = TEST_OK; + + /* mutex lock */ + pthread_mutex_lock(&multi_mutex); + char *uri = ico_uws_get_uri(srv_t->context); + /* mutex unlock */ + pthread_mutex_unlock(&multi_mutex); + if (strcmp(uri, srv_t->uri) != 0) { + ret_str = TEST_NG; + } + dbg_print("ico_uws_get_uri [%s] (server %d) : %s\n", + uri, srv_t->id, ret_str); + + return; +} + +/* send data */ +static void +tst_send(struct tst_server_t *srv_t) +{ + int i; + + for (i = 0; i < 10; i++) { + /* mutex lock */ + pthread_mutex_lock(&multi_mutex); + ico_uws_service(srv_t->context); + /* mutex unlock */ + pthread_mutex_unlock(&multi_mutex); + usleep(100); + } + + /* mutex lock */ + pthread_mutex_lock(&multi_mutex); + ico_uws_send(srv_t->context, srv_t->srv_id, srv_t->data, srv_t->len); + /* mutex unlock */ + pthread_mutex_unlock(&multi_mutex); + + return; +} + +/* service loop (wait to receive data) */ +static void +tst_service_receive(struct tst_server_t *srv_t) +{ + char *ret_str = TEST_OK; + + while (srv_t->receive_flag == UNSET_FLAG) { + /* mutex lock */ + pthread_mutex_lock(&multi_mutex); + ico_uws_service(srv_t->context); + /* mutex unlock */ + pthread_mutex_unlock(&multi_mutex); + usleep(50); + } + srv_t->receive_flag = UNSET_FLAG; + dbg_print("ico_uws_service (server %d received) : %s\n", srv_t->id, ret_str); + + return; +} + +/* service loop (wait to close connection) */ +static void +tst_service_close(struct tst_server_t *srv_t) +{ + char *ret_str = TEST_OK; + + while (srv_t->close_flag == UNSET_FLAG) { + /* mutex lock */ + pthread_mutex_lock(&multi_mutex); + ico_uws_service(srv_t->context); + /* mutex unlock */ + pthread_mutex_unlock(&multi_mutex); + usleep(50); + } + srv_t->close_flag = UNSET_FLAG; + dbg_print("ico_uws_service (server %d close) : %s\n", srv_t->id, ret_str); + + return; +} + +/* close */ +static void +tst_close(struct tst_server_t *srv_t) +{ + char *ret_str = TEST_OK; + + /* mutex lock */ + pthread_mutex_lock(&multi_mutex); + ico_uws_close(srv_t->context); + /* mutex unlock */ + pthread_mutex_unlock(&multi_mutex); + + dbg_print("ico_uws_close (server %d) : %s\n", srv_t->id, ret_str); + + return; +} + +/* set callback */ +static void +tst_set_evt_callback(struct tst_server_t *srv_t) +{ + int ret; + char *ret_str = TEST_OK; + + srv_t->set_cb_flag = SET_FLAG; + /* mutex lock */ + pthread_mutex_lock(&multi_mutex); + /* set callback */ + ret = ico_uws_set_event_cb(srv_t->context, tst_uws_callback, NULL); + /* mutex unlock */ + pthread_mutex_unlock(&multi_mutex); + if (ret != ICO_UWS_ERR_NONE) { + ret_str = TEST_NG; + dbg_print("ico_uws_set_event_cb (server %d) : %s (%d)\n", + srv_t->id, ret_str, ret); + return; + } + + dbg_print("ico_uws_set_event_cb (server %d) : %s\n", + srv_t->id, ret_str); + + return; +} + +/* unset callback */ +static void +tst_unset_evt_callback(struct tst_server_t *srv_t) +{ + char *ret_str = TEST_OK; + + /* mutex lock */ + pthread_mutex_lock(&multi_mutex); + /* unset callback */ + ico_uws_unset_event_cb(srv_t->context); + /* mutex unlock */ + pthread_mutex_unlock(&multi_mutex); + + srv_t->set_cb_flag = UNSET_FLAG; + srv_t->num_call_cb = 0; + + /* mutex lock */ + pthread_mutex_lock(&multi_mutex); + /* occurs the error event */ + (void)ico_uws_get_uri(NULL); + /* mutex unlock */ + pthread_mutex_unlock(&multi_mutex); + sleep(SLEEP_TIME); + if (srv_t->num_call_cb > 0) { + ret_str = TEST_NG; + } + + dbg_print("ico_uws_unset_event_cb (server %d) : %s\n", + srv_t->id, ret_str); + + return; +} + +/* prepare for test */ +static struct tst_server_t * +ico_uws_server_test_init(int id) +{ + struct tst_server_t *srv_t; + + srv_t = calloc(1, sizeof(struct tst_server_t)); + if (srv_t == NULL) { + printf("calloc failed\n"); + return NULL; + } + + /* set uri to connect to */ + sprintf(srv_t->uri, ":%s", srv_ports[id]); + + /* initialize */ + srv_t->context = NULL; + srv_t->srv_id = NULL; + srv_t->receive_flag = UNSET_FLAG; + srv_t->close_flag = UNSET_FLAG; + srv_t->set_cb_flag = UNSET_FLAG; + srv_t->num_call_cb = 0; + srv_t->id = id; + + return srv_t; +} + +/* ----------------------------------------------- */ +/* Test Main */ +/* ----------------------------------------------- */ +static void * +tst_server_thread(void *args) +{ + /* prepare for test */ + first_srv = ico_uws_server_test_init(0); + if (first_srv == NULL) { + return NULL; + } + + /* create context */ + tst_create_context(first_srv); + + if (first_srv->context != NULL) { + /* set callback */ + tst_set_evt_callback(first_srv); + + /* check the state */ + tst_get_ready_state(first_srv, ICO_UWS_STATE_CONNECTING, + "connecting"); + + /* check the uri */ + tst_get_uri(first_srv); + + /* wait to receive data */ + tst_service_receive(first_srv); + + /* send data */ + tst_send(first_srv); + + /* wait to close connection */ + tst_service_close(first_srv); + + /* check the state */ + tst_get_ready_state(first_srv, ICO_UWS_STATE_CLOSED, "close"); + + /* interval */ + sleep(SLEEP_TIME); + + /* unset callback */ + tst_unset_evt_callback(first_srv); + + /* interval */ + sleep(SLEEP_TIME); + + /* session close */ + tst_close(first_srv); + } + /* free memory */ + free(first_srv); + + return NULL; +} + +static void * +tst_server_thread_sec(void *args) +{ + /* prepare for test */ + second_srv = ico_uws_server_test_init(1); + if (second_srv == NULL) { + return NULL; + } + + /* create context */ + tst_create_context(second_srv); + + if (second_srv->context != NULL) { + /* set callback */ + tst_set_evt_callback(second_srv); + + /* check the state */ + tst_get_ready_state(second_srv, ICO_UWS_STATE_CONNECTING, + "connecting"); + + /* check the uri */ + tst_get_uri(second_srv); + + /* wait to receive data */ + tst_service_receive(second_srv); + + /* send data */ + tst_send(second_srv); + + /* wait to close connection */ + tst_service_close(second_srv); + + /* check the state */ + tst_get_ready_state(second_srv, ICO_UWS_STATE_CLOSED, "close"); + + /* interval */ + sleep(SLEEP_TIME); + + /* unset callback */ + tst_unset_evt_callback(second_srv); + + /* interval */ + sleep(SLEEP_TIME); + + /* session close */ + tst_close(second_srv); + } + + /* free memory */ + free(second_srv); + + return NULL; +} + +/* test main (to connect to multi servers) */ +static int +ico_uws_server_test_multi() +{ + pthread_t thread, thread_sec; + + /* server to connect server (port: 8080) */ + pthread_create( &thread, NULL, tst_server_thread, (void *)NULL ); + /* server to connect server (port: 9090) */ + pthread_create( &thread_sec, NULL, tst_server_thread_sec, (void *)NULL ); + + pthread_join( thread, NULL ); + pthread_join( thread_sec, NULL ); + + /* interval */ + sleep(SLEEP_TIME); + + return 1; +} + +/* ----------------------------------------------- */ +/* Main */ +/* ----------------------------------------------- */ +static GMainLoop *g_mainloop = NULL; + +static gboolean +exit_program(gpointer data) +{ + g_main_loop_quit(g_mainloop); + + return FALSE; +} + +/* main */ +int +main(int argc, char **argv) +{ + g_setenv("PKG_NAME", "org.tizen.ico.tst_ico_uws_mlt_server", 1); + g_mainloop = g_main_loop_new(NULL, 0); + + printf("\n"); + printf("##### ico_uws API (server to listen to multi clients)"); + printf(" Test Start #####\n"); + ico_uws_server_test_multi(); + printf("##### ico_uws API (server to listen to multi clients)"); + printf(" Test End #####\n"); + printf("\n"); + + g_timeout_add_seconds(2, exit_program, NULL); + g_main_loop_run(g_mainloop); + + return 0; +} diff --git a/test/tst_ico_uws_server.c b/test/tst_ico_uws_server.c new file mode 100644 index 0000000..28d5c40 --- /dev/null +++ b/test/tst_ico_uws_server.c @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2013, TOYOTA MOTOR CORPORATION. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ +/** + * @brief test server (socket library for communicate) + * + * @date June-7-2013 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <glib.h> +#include "ico_uws.h" + +#include "tst_ico_uws.h" + +/* ----------------------------------------------- */ +/* Variable */ +/* ----------------------------------------------- */ +#define SLEEP_TIME 2 + +/* context */ +static struct ico_uws_context *context; + +/* close event check */ +static int close_flag = UNSET_FLAG; +static int num_connect = 0; + +/* callback function is setting or not setting */ +static int set_cb_flag = UNSET_FLAG; +static int num_call_cb = 0; + +/* ----------------------------------------------- */ +/* Define of static function */ +/* ----------------------------------------------- */ +static void tst_uws_callback(const struct ico_uws_context *context, + const ico_uws_evt_e event, + const void *id, + const ico_uws_detail *detail, + void *user_data); +static void tst_create_context(char *uri); +static void tst_get_ready_state(int state, char *str_state); +static void tst_get_uri(char *set_uri); +static void tst_service(void); +static void tst_close(void); +static void tst_set_evt_callback(void); +static void tst_unset_evt_callback(void); +static int ico_uws_server_test(char *uri); + +/* ----------------------------------------------- */ +/* Public API Test */ +/* ----------------------------------------------- */ +/* event callback */ +static void +tst_uws_callback(const struct ico_uws_context *context, + const ico_uws_evt_e event, + const void *id, + const ico_uws_detail *detail, + void *user_data) +{ + char str[256]; + char *ret_str; + + num_call_cb++; + if (set_cb_flag == SET_FLAG) { + ret_str = TEST_OK; + } + else { + ret_str = TEST_NG; + } + + switch (event) { + case ICO_UWS_EVT_OPEN: + sprintf(str, "open"); + num_connect++; + if (context != NULL) { + tst_get_ready_state(ICO_UWS_STATE_OPEN, "open"); + } + break; + case ICO_UWS_EVT_ERROR: + sprintf(str, "error"); + break; + case ICO_UWS_EVT_CLOSE: + sprintf(str, "close"); + num_connect--; + if (num_connect == 0) { + close_flag = SET_FLAG; + } + if (context != NULL) { + tst_get_ready_state(ICO_UWS_STATE_CLOSED, "close"); + } + break; + case ICO_UWS_EVT_RECEIVE: + sprintf(str, "receive"); + ico_uws_send((struct ico_uws_context *)context, + (void *)id, + (unsigned char *)detail->_ico_uws_message.recv_data, + detail->_ico_uws_message.recv_len); + sprintf(str, "%s '%s'", + str, (unsigned char *)detail->_ico_uws_message.recv_data); + break; + case ICO_UWS_EVT_ADD_FD: + sprintf(str, "add fd(%d)", detail->_ico_uws_fd.fd); + break; + case ICO_UWS_EVT_DEL_FD: + sprintf(str, "delete fd(%d)", detail->_ico_uws_fd.fd); + break; + default: + /* other event is not test */ + break; + } + dbg_print("ico_uws_evt_cb [%d (%s)] (server) : %s\n", + event, str, ret_str); + + return; +} + +/* create context */ +static void +tst_create_context(char *uri) +{ + char *ret_str = TEST_OK; + + context = ico_uws_create_context(uri, PROTOCOL_NAME); + if (context == NULL) { + ret_str = TEST_NG; + } + num_connect = 0; + dbg_print("ico_uws_create_context (server) : %s\n", ret_str); + + return; +} + +/* get ready state */ +static void +tst_get_ready_state(int state, char *str_state) +{ + char *ret_str = TEST_OK; + + ico_uws_state_e cur_state = ico_uws_get_ready_state(context); + if (cur_state != state) { + ret_str = TEST_NG; + } + dbg_print("ico_uws_get_ready_state [%s] (server) : %s\n", + str_state, ret_str); + + return; +} + +/* get uri */ +static void +tst_get_uri(char *set_uri) +{ + char *ret_str = TEST_OK; + + char *uri = ico_uws_get_uri(context); + if (strcmp(uri, set_uri) != 0) { + ret_str = TEST_NG; + } + dbg_print("ico_uws_get_uri [%s] (server) : %s\n", + uri, ret_str); + + return; +} + +/* service loop */ +static void +tst_service() +{ + char *ret_str = TEST_OK; + + close_flag = UNSET_FLAG; + /* wait to close the connection */ + while (close_flag == UNSET_FLAG) { + ico_uws_service(context); + usleep(50); + } + dbg_print("ico_uws_service (server) : %s\n", ret_str); + + return; +} + +/* close */ +static void +tst_close() +{ + char *ret_str = TEST_OK; + + ico_uws_close(context); + dbg_print("ico_uws_close (server) : %s\n", ret_str); + + return; +} + +/* set callback */ +static void +tst_set_evt_callback() +{ + int ret; + char *ret_str = TEST_OK; + + /* set callback */ + set_cb_flag = SET_FLAG; + ret = ico_uws_set_event_cb(context, tst_uws_callback, NULL); + if (ret != ICO_UWS_ERR_NONE) { + ret_str = TEST_NG; + dbg_print("ico_uws_set_event_cb (server) : %s (%d)\n", + ret_str, ret); + return; + } + + dbg_print("ico_uws_set_event_cb (server) : %s\n", ret_str); + + return; +} + +/* unset callback */ +static void +tst_unset_evt_callback() +{ + char *ret_str = TEST_OK; + + /* unset callback */ + ico_uws_unset_event_cb(context); + set_cb_flag = UNSET_FLAG; + num_call_cb = 0; + + /* occurs the error event */ + (void)ico_uws_get_uri(NULL); + sleep(SLEEP_TIME); + if (num_call_cb > 0) { + ret_str = TEST_NG; + } + + dbg_print("ico_uws_unset_event_cb (server) : %s\n", ret_str); + + return; +} + +/* test main (server) */ +static int +ico_uws_server_test(char *uri) +{ + /* create context */ + tst_create_context(uri); + + if (context) { + /* set callback */ + tst_set_evt_callback(); + + /* client does not connect */ + tst_get_ready_state(ICO_UWS_STATE_CONNECTING, "connecting"); + + /* get uri */ + tst_get_uri(uri); + + /* service (loop) */ + tst_service(); + + /* interval */ + sleep(SLEEP_TIME); + + /* unset callback */ + tst_unset_evt_callback(); + + /* close */ + tst_close(); + } + + return 1; +} + +/* ----------------------------------------------- */ +/* Main */ +/* ----------------------------------------------- */ +static GMainLoop *g_mainloop = NULL; + +static gboolean +exit_program(gpointer data) +{ + g_main_loop_quit(g_mainloop); + + return FALSE; +} + +/* main */ +int +main(int argc, char **argv) +{ + char uri[256]; + int id; + int set_uri_flag = UNSET_FLAG; + + for (id = 0; id < argc; id++) { + if (strcmp(argv[id], "-p") == 0) { + id++; + sprintf(uri, ":%s", argv[id]); + set_uri_flag = SET_FLAG; + } + } + + /* set default uri */ + if (set_uri_flag == UNSET_FLAG) { + sprintf(uri, ":%s", SRV_PORT); + } + + g_setenv("PKG_NAME", "org.tizen.ico.tst_ico_uws_server", 1); + g_mainloop = g_main_loop_new(NULL, 0); + + printf("\n"); + printf("##### ico_uws API (server) Test Start #####\n"); + ico_uws_server_test(uri); + printf("##### ico_uws API (server) Test End #####\n"); + printf("\n"); + + g_timeout_add_seconds(2, exit_program, NULL); + g_main_loop_run(g_mainloop); + + return 0; +} |