Import initial.
authorShibata Makoto <shibata@mac.tec.toyota.co.jp>
Wed, 17 Jul 2013 10:44:53 +0000 (19:44 +0900)
committerShibata Makoto <shibata@mac.tec.toyota.co.jp>
Wed, 17 Jul 2013 10:47:16 +0000 (19:47 +0900)
Change-Id: I4f2d6a1747e069582626f68346ae97744a5584c4
Signed-off-by: Shibata Makoto <shibata@mac.tec.toyota.co.jp>
16 files changed:
Makefile.am [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
configure.ac [new file with mode: 0644]
include/ico_uws.h [new file with mode: 0644]
include/ico_uws_private.h [new file with mode: 0644]
packaging/ico-uxf-utilities.changes [new file with mode: 0644]
packaging/ico-uxf-utilities.spec [new file with mode: 0644]
src/Makefile.am [new file with mode: 0644]
src/ico_uws.c [new file with mode: 0644]
test/Makefile.am [new file with mode: 0644]
test/run_test.sh [new file with mode: 0755]
test/tst_ico_uws.h [new file with mode: 0644]
test/tst_ico_uws_client.c [new file with mode: 0644]
test/tst_ico_uws_multi_client.c [new file with mode: 0644]
test/tst_ico_uws_multi_server.c [new file with mode: 0644]
test/tst_ico_uws_server.c [new file with mode: 0644]

diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..0b75466
--- /dev/null
@@ -0,0 +1,4 @@
+SUBDIRS = src test
+
+DIST_SUBDIRS = src test
+
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..916169a
--- /dev/null
@@ -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 (file)
index 0000000..40e85fd
--- /dev/null
@@ -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 (file)
index 0000000..bc53f68
--- /dev/null
@@ -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 (file)
index 0000000..dec4dc1
--- /dev/null
@@ -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 (file)
index 0000000..16fe129
--- /dev/null
@@ -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 (file)
index 0000000..3432960
--- /dev/null
@@ -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 (file)
index 0000000..63c73e1
--- /dev/null
@@ -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 (file)
index 0000000..eb36701
--- /dev/null
@@ -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 (file)
index 0000000..45f88b6
--- /dev/null
@@ -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 (executable)
index 0000000..dd9c881
--- /dev/null
@@ -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 (file)
index 0000000..9c33f05
--- /dev/null
@@ -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 (file)
index 0000000..7647e9c
--- /dev/null
@@ -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 (file)
index 0000000..06fab7d
--- /dev/null
@@ -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 (file)
index 0000000..ba928d1
--- /dev/null
@@ -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 (file)
index 0000000..28d5c40
--- /dev/null
@@ -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;
+}