Просмотр исходного кода

Added socket send and recv timeout options (#1419)

Added socket send and recv timeout options with implementation for posix platform.

This is part of a extending support for sockets in WASI. #1336.

Also add sample that sets and reads back the send and receive timeouts using
the native function binding.
Callum Macmillan 3 лет назад
Родитель
Сommit
72367f47eb

+ 67 - 0
core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h

@@ -94,6 +94,9 @@ typedef struct __wasi_addr_info_hints_t {
  * <sys/types.h>
  */
 
+#define SO_RCVTIMEO 20
+#define SO_SNDTIMEO 21
+
 struct addrinfo {
     int ai_flags;             /* Input flags.  */
     int ai_family;            /* Protocol family for socket.  */
@@ -132,6 +135,14 @@ getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
 int
 getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
 
+int
+getsockopt(int sockfd, int level, int optname, void *__restrict optval,
+           socklen_t *__restrict optlen);
+
+int
+setsockopt(int sockfd, int level, int optname, const void *optval,
+           socklen_t optlen);
+
 int
 getaddrinfo(const char *node, const char *service, const struct addrinfo *hints,
             struct addrinfo **res);
@@ -463,6 +474,62 @@ __wasi_sock_set_send_buf_size(__wasi_fd_t fd)
         __imported_wasi_snapshot_preview1_sock_set_send_buf_size((int32_t)fd);
 }
 
+int32_t
+__imported_wasi_snapshot_preview1_sock_get_recv_timeout(int32_t arg0,
+                                                        int32_t arg1)
+    __attribute__((__import_module__("wasi_snapshot_preview1"),
+                   __import_name__("sock_get_recv_timeout")));
+
+static inline __wasi_errno_t
+__wasi_sock_get_recv_timeout(__wasi_fd_t fd, uint64_t *timeout_us)
+{
+    return (__wasi_errno_t)
+        __imported_wasi_snapshot_preview1_sock_get_recv_timeout(
+            (int32_t)fd, (int32_t)timeout_us);
+}
+
+int32_t
+__imported_wasi_snapshot_preview1_sock_set_recv_timeout(int32_t arg0,
+                                                        int64_t arg1)
+    __attribute__((__import_module__("wasi_snapshot_preview1"),
+                   __import_name__("sock_set_recv_timeout")));
+
+static inline __wasi_errno_t
+__wasi_sock_set_recv_timeout(__wasi_fd_t fd, uint64_t timeout_us)
+{
+    return (__wasi_errno_t)
+        __imported_wasi_snapshot_preview1_sock_set_recv_timeout(
+            (int32_t)fd, (int64_t)timeout_us);
+}
+
+int32_t
+__imported_wasi_snapshot_preview1_sock_get_send_timeout(int32_t arg0,
+                                                        int32_t arg1)
+    __attribute__((__import_module__("wasi_snapshot_preview1"),
+                   __import_name__("sock_get_send_timeout")));
+
+static inline __wasi_errno_t
+__wasi_sock_get_send_timeout(__wasi_fd_t fd, uint64_t *timeout_us)
+{
+    return (__wasi_errno_t)
+        __imported_wasi_snapshot_preview1_sock_get_send_timeout(
+            (int32_t)fd, (int32_t)timeout_us);
+}
+
+int32_t
+__imported_wasi_snapshot_preview1_sock_set_send_timeout(int32_t arg0,
+                                                        int64_t arg1)
+    __attribute__((__import_module__("wasi_snapshot_preview1"),
+                   __import_name__("sock_set_send_timeout")));
+
+static inline __wasi_errno_t
+__wasi_sock_set_send_timeout(__wasi_fd_t fd, uint64_t timeout_us)
+{
+    return (__wasi_errno_t)
+        __imported_wasi_snapshot_preview1_sock_set_send_timeout(
+            (int32_t)fd, (int64_t)timeout_us);
+}
+
 /**
  * TODO: modify recv() and send()
  * since don't want to re-compile the wasi-libc,

+ 90 - 1
core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c

@@ -469,4 +469,93 @@ freeaddrinfo(struct addrinfo *res)
      * of aibuf array allocated in getaddrinfo, therefore this call
      * frees the memory of the entire array. */
     free(res);
-}
+}
+
+struct timeval
+time_us_to_timeval(uint64_t time_us)
+{
+    struct timeval tv;
+    tv.tv_sec = time_us / 1000000UL;
+    tv.tv_usec = time_us % 1000000UL;
+    return tv;
+}
+
+uint64_t
+timeval_to_time_us(struct timeval tv)
+{
+    return (tv.tv_sec * 1000000UL) + tv.tv_usec;
+}
+
+int
+get_sol_socket_option(int sockfd, int optname, void *__restrict optval,
+                      socklen_t *__restrict optlen)
+{
+    __wasi_errno_t error;
+    uint64_t timeout_us;
+
+    switch (optname) {
+        case SO_RCVTIMEO:
+            assert(*optlen == sizeof(struct timeval));
+            error = __wasi_sock_get_recv_timeout(sockfd, &timeout_us);
+            HANDLE_ERROR(error);
+            *(struct timeval *)optval = time_us_to_timeval(timeout_us);
+            return error;
+        case SO_SNDTIMEO:
+            assert(*optlen == sizeof(struct timeval));
+            error = __wasi_sock_get_send_timeout(sockfd, &timeout_us);
+            HANDLE_ERROR(error);
+            *(struct timeval *)optval = time_us_to_timeval(timeout_us);
+            return error;
+    }
+
+    HANDLE_ERROR(__WASI_ERRNO_NOTSUP);
+}
+
+int
+getsockopt(int sockfd, int level, int optname, void *__restrict optval,
+           socklen_t *__restrict optlen)
+{
+    switch (level) {
+        case SOL_SOCKET:
+            return get_sol_socket_option(sockfd, optname, optval, optlen);
+    }
+
+    HANDLE_ERROR(__WASI_ERRNO_NOTSUP);
+}
+
+int
+set_sol_socket_option(int sockfd, int optname, const void *optval,
+                      socklen_t optlen)
+{
+    __wasi_errno_t error;
+    uint64_t timeout_us;
+
+    switch (optname) {
+        case SO_RCVTIMEO:
+            assert(optlen == sizeof(struct timeval));
+            timeout_us = timeval_to_time_us(*(struct timeval *)optval);
+            error = __wasi_sock_set_recv_timeout(sockfd, timeout_us);
+            HANDLE_ERROR(error);
+            return error;
+        case SO_SNDTIMEO:
+            assert(optlen == sizeof(struct timeval));
+            timeout_us = timeval_to_time_us(*(struct timeval *)optval);
+            error = __wasi_sock_set_send_timeout(sockfd, timeout_us);
+            HANDLE_ERROR(error);
+            return error;
+    }
+
+    HANDLE_ERROR(__WASI_ERRNO_NOTSUP);
+}
+
+int
+setsockopt(int sockfd, int level, int optname, const void *optval,
+           socklen_t optlen)
+{
+    switch (level) {
+        case SOL_SOCKET:
+            return set_sol_socket_option(sockfd, optname, optval, optlen);
+    }
+
+    HANDLE_ERROR(__WASI_ERRNO_NOTSUP);
+}

+ 74 - 0
core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c

@@ -1126,6 +1126,25 @@ wasi_sock_get_recv_buf_size(wasm_exec_env_t exec_env, wasi_fd_t fd,
     return __WASI_ENOSYS;
 }
 
+static wasi_errno_t
+wasi_sock_get_recv_timeout(wasm_exec_env_t exec_env, wasi_fd_t fd,
+                           uint64_t *timeout_us)
+{
+    wasm_module_inst_t module_inst = get_module_inst(exec_env);
+    wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst);
+    struct fd_table *curfds = NULL;
+
+    if (!wasi_ctx)
+        return __WASI_EACCES;
+
+    if (!validate_native_addr(timeout_us, sizeof(uint64_t)))
+        return __WASI_EINVAL;
+
+    curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
+
+    return wasmtime_ssp_sock_get_recv_timeout(curfds, fd, timeout_us);
+}
+
 static wasi_errno_t
 wasi_sock_get_reuse_addr(wasm_exec_env_t exec_env, wasi_fd_t fd, uint8 *reuse)
 {
@@ -1145,6 +1164,25 @@ wasi_sock_get_send_buf_size(wasm_exec_env_t exec_env, wasi_fd_t fd,
     return __WASI_ENOSYS;
 }
 
+static wasi_errno_t
+wasi_sock_get_send_timeout(wasm_exec_env_t exec_env, wasi_fd_t fd,
+                           uint64_t *timeout_us)
+{
+    wasm_module_inst_t module_inst = get_module_inst(exec_env);
+    wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst);
+    struct fd_table *curfds = NULL;
+
+    if (!wasi_ctx)
+        return __WASI_EACCES;
+
+    if (!validate_native_addr(timeout_us, sizeof(uint64_t)))
+        return __WASI_EINVAL;
+
+    curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
+
+    return wasmtime_ssp_sock_get_send_timeout(curfds, fd, timeout_us);
+}
+
 static wasi_errno_t
 wasi_sock_listen(wasm_exec_env_t exec_env, wasi_fd_t fd, uint32 backlog)
 {
@@ -1184,6 +1222,22 @@ wasi_sock_set_recv_buf_size(wasm_exec_env_t exec_env, wasi_fd_t fd,
     return __WASI_ENOSYS;
 }
 
+static wasi_errno_t
+wasi_sock_set_recv_timeout(wasm_exec_env_t exec_env, wasi_fd_t fd,
+                           uint64_t timeout_us)
+{
+    wasm_module_inst_t module_inst = get_module_inst(exec_env);
+    wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst);
+    struct fd_table *curfds = NULL;
+
+    if (!wasi_ctx)
+        return __WASI_EACCES;
+
+    curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
+
+    return wasmtime_ssp_sock_set_recv_timeout(curfds, fd, timeout_us);
+}
+
 static wasi_errno_t
 wasi_sock_set_reuse_addr(wasm_exec_env_t exec_env, wasi_fd_t fd, uint8 reuse)
 {
@@ -1203,6 +1257,22 @@ wasi_sock_set_send_buf_size(wasm_exec_env_t exec_env, wasi_fd_t fd,
     return __WASI_ENOSYS;
 }
 
+static wasi_errno_t
+wasi_sock_set_send_timeout(wasm_exec_env_t exec_env, wasi_fd_t fd,
+                           uint64_t timeout_us)
+{
+    wasm_module_inst_t module_inst = get_module_inst(exec_env);
+    wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst);
+    struct fd_table *curfds = NULL;
+
+    if (!wasi_ctx)
+        return __WASI_EACCES;
+
+    curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx);
+
+    return wasmtime_ssp_sock_set_send_timeout(curfds, fd, timeout_us);
+}
+
 static wasi_errno_t
 wasi_sock_recv(wasm_exec_env_t exec_env, wasi_fd_t sock, iovec_app_t *ri_data,
                uint32 ri_data_len, wasi_riflags_t ri_flags, uint32 *ro_data_len,
@@ -1427,17 +1497,21 @@ static NativeSymbol native_symbols_libc_wasi[] = {
     REG_NATIVE_FUNC(sock_close, "(i)i"),
     REG_NATIVE_FUNC(sock_connect, "(i*)i"),
     REG_NATIVE_FUNC(sock_get_recv_buf_size, "(i*)i"),
+    REG_NATIVE_FUNC(sock_get_recv_timeout, "(i*)i"),
     REG_NATIVE_FUNC(sock_get_reuse_addr, "(i*)i"),
     REG_NATIVE_FUNC(sock_get_reuse_port, "(i*)i"),
     REG_NATIVE_FUNC(sock_get_send_buf_size, "(i*)i"),
+    REG_NATIVE_FUNC(sock_get_send_timeout, "(i*)i"),
     REG_NATIVE_FUNC(sock_listen, "(ii)i"),
     REG_NATIVE_FUNC(sock_open, "(iii*)i"),
     REG_NATIVE_FUNC(sock_recv, "(i*ii**)i"),
     REG_NATIVE_FUNC(sock_send, "(i*ii*)i"),
     REG_NATIVE_FUNC(sock_set_recv_buf_size, "(ii)i"),
+    REG_NATIVE_FUNC(sock_set_recv_timeout, "(iI)i"),
     REG_NATIVE_FUNC(sock_set_reuse_addr, "(ii)i"),
     REG_NATIVE_FUNC(sock_set_reuse_port, "(ii)i"),
     REG_NATIVE_FUNC(sock_set_send_buf_size, "(ii)i"),
+    REG_NATIVE_FUNC(sock_set_send_timeout, "(iI)i"),
     REG_NATIVE_FUNC(sock_shutdown, "(ii)i"),
     REG_NATIVE_FUNC(sched_yield, "()i"),
 };

+ 32 - 0
core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/include/wasmtime_ssp.h

@@ -1088,6 +1088,38 @@ __wasi_errno_t wasmtime_ssp_sock_shutdown(
     __wasi_fd_t sock
 ) WASMTIME_SSP_SYSCALL_NAME(sock_shutdown) __attribute__((__warn_unused_result__));
 
+__wasi_errno_t wasmtime_ssp_sock_set_recv_timeout(
+#if !defined(WASMTIME_SSP_STATIC_CURFDS)
+    struct fd_table *curfds,
+#endif
+    __wasi_fd_t sock,
+    uint64_t timeout_us
+) WASMTIME_SSP_SYSCALL_NAME(sock_set_recv_timeout) __attribute__((__warn_unused_result__));
+
+__wasi_errno_t wasmtime_ssp_sock_get_recv_timeout(
+#if !defined(WASMTIME_SSP_STATIC_CURFDS)
+    struct fd_table *curfds,
+#endif
+    __wasi_fd_t sock,
+    uint64_t *timeout_us
+) WASMTIME_SSP_SYSCALL_NAME(sock_get_recv_timeout) __attribute__((__warn_unused_result__));
+
+__wasi_errno_t wasmtime_ssp_sock_set_send_timeout(
+#if !defined(WASMTIME_SSP_STATIC_CURFDS)
+    struct fd_table *curfds,
+#endif
+    __wasi_fd_t sock,
+    uint64_t timeout_us
+) WASMTIME_SSP_SYSCALL_NAME(sock_set_send_timeout) __attribute__((__warn_unused_result__));
+
+__wasi_errno_t wasmtime_ssp_sock_get_send_timeout(
+#if !defined(WASMTIME_SSP_STATIC_CURFDS)
+    struct fd_table *curfds,
+#endif
+    __wasi_fd_t sock,
+    uint64_t *timeout_us
+) WASMTIME_SSP_SYSCALL_NAME(sock_get_send_timeout) __attribute__((__warn_unused_result__));
+
 __wasi_errno_t wasmtime_ssp_sched_yield(void)
     WASMTIME_SSP_SYSCALL_NAME(sched_yield) __attribute__((__warn_unused_result__));
 

+ 34 - 0
core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.c

@@ -3556,3 +3556,37 @@ addr_pool_destroy(struct addr_pool *addr_pool)
         cur = next;
     }
 }
+
+#ifndef WASMTIME_SSP_STATIC_CURFDS
+#define WASMTIME_SSP_PASSTHROUGH_FD_TABLE struct fd_table *curfds,
+#else
+#define WASMTIME_SSP_PASSTHROUGH_FD_TABLE
+#endif
+
+// Defines a function that passes through the socket option to the OS
+// implementation
+#define WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(FUNC_NAME, OPTION_TYPE) \
+    __wasi_errno_t wasmtime_ssp_sock_##FUNC_NAME(                      \
+        WASMTIME_SSP_PASSTHROUGH_FD_TABLE __wasi_fd_t sock,            \
+        OPTION_TYPE option)                                            \
+    {                                                                  \
+        struct fd_object *fo;                                          \
+        __wasi_errno_t error;                                          \
+        int ret;                                                       \
+        error = fd_object_get(curfds, &fo, sock, 0, 0);                \
+        if (error != 0)                                                \
+            return error;                                              \
+        ret = os_socket_##FUNC_NAME(fd_number(fo), option);            \
+        fd_object_release(fo);                                         \
+        if (BHT_OK != ret)                                             \
+            return convert_errno(errno);                               \
+        return __WASI_ESUCCESS;                                        \
+    }
+
+WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(set_send_timeout, uint64)
+WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(get_send_timeout, uint64 *)
+WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(set_recv_timeout, uint64)
+WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(get_recv_timeout, uint64 *)
+
+#undef WASMTIME_SSP_PASSTHROUGH_FD_TABLE
+#undef WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION

+ 48 - 0
core/shared/platform/common/posix/posix_socket.c

@@ -351,6 +351,54 @@ os_socket_convert_sockaddr(struct sockaddr *addr, uint8_t *buf, size_t buflen,
     return BHT_OK;
 }
 
+int
+os_socket_set_send_timeout(bh_socket_t socket, uint64 timeout_us)
+{
+    struct timeval tv;
+    tv.tv_sec = timeout_us / 1000000UL;
+    tv.tv_usec = timeout_us % 1000000UL;
+    if (setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) != 0) {
+        return BHT_ERROR;
+    }
+    return BHT_OK;
+}
+
+int
+os_socket_get_send_timeout(bh_socket_t socket, uint64 *timeout_us)
+{
+    struct timeval tv;
+    socklen_t tv_len = sizeof(tv);
+    if (getsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &tv, &tv_len) != 0) {
+        return BHT_ERROR;
+    }
+    *timeout_us = (tv.tv_sec * 1000000UL) + tv.tv_usec;
+    return BHT_OK;
+}
+
+int
+os_socket_set_recv_timeout(bh_socket_t socket, uint64 timeout_us)
+{
+    struct timeval tv;
+    tv.tv_sec = timeout_us / 1000000UL;
+    tv.tv_usec = timeout_us % 1000000UL;
+    if (setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0) {
+        return BHT_ERROR;
+    }
+    return BHT_OK;
+}
+
+int
+os_socket_get_recv_timeout(bh_socket_t socket, uint64 *timeout_us)
+{
+    struct timeval tv;
+    socklen_t tv_len = sizeof(tv);
+    if (getsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &tv, &tv_len) != 0) {
+        return BHT_ERROR;
+    }
+    *timeout_us = (tv.tv_sec * 1000000UL) + tv.tv_usec;
+    return BHT_OK;
+}
+
 int
 os_socket_addr_local(bh_socket_t socket, uint8_t *buf, size_t buflen,
                      uint16_t *port, uint8_t *is_ipv4)

+ 44 - 0
core/shared/platform/include/platform_api_extension.h

@@ -511,6 +511,50 @@ int
 os_socket_addr_remote(bh_socket_t socket, uint8_t *buf, size_t buflen,
                       uint16_t *port, uint8_t *is_ipv4);
 
+/**
+ * Set the send timeout until reporting an error
+ *
+ * @param socket the socket to set
+ * @param time_us microseconds until timeout
+ *
+ * @return 0 if success, -1 otherwise
+ */
+int
+os_socket_set_send_timeout(bh_socket_t socket, uint64 timeout_us);
+
+/**
+ * Get the send timeout until reporting an error
+ *
+ * @param socket the socket to set
+ * @param time_us the returned microseconds until timeout
+ *
+ * @return 0 if success, -1 otherwise
+ */
+int
+os_socket_get_send_timeout(bh_socket_t socket, uint64 *timeout_us);
+
+/**
+ * Set the recv timeout until reporting an error
+ *
+ * @param socket the socket to set
+ * @param time_us microseconds until timeout
+ *
+ * @return 0 if success, -1 otherwise
+ */
+int
+os_socket_set_recv_timeout(bh_socket_t socket, uint64 timeout_us);
+
+/**
+ * Get the recv timeout until reporting an error
+ *
+ * @param socket the socket to set
+ * @param time_us the returned microseconds until timeout
+ *
+ * @return 0 if success, -1 otherwise
+ */
+int
+os_socket_get_recv_timeout(bh_socket_t socket, uint64 *timeout_us);
+
 #ifdef __cplusplus
 }
 #endif

+ 32 - 0
core/shared/platform/linux-sgx/sgx_socket.c

@@ -713,4 +713,36 @@ os_socket_addr_remote(bh_socket_t socket, uint8_t *buf, size_t buflen,
     return BHT_ERROR;
 }
 
+int
+os_socket_set_send_timeout(bh_socket_t socket, uint64 timeout_us)
+{
+    errno = ENOSYS;
+
+    return BHT_ERROR;
+}
+
+int
+os_socket_get_send_timeout(bh_socket_t socket, uint64 *timeout_us)
+{
+    errno = ENOSYS;
+
+    return BHT_ERROR;
+}
+
+int
+os_socket_set_recv_timeout(bh_socket_t socket, uint64 timeout_us)
+{
+    errno = ENOSYS;
+
+    return BHT_ERROR;
+}
+
+int
+os_socket_get_recv_timeout(bh_socket_t socket, uint64 *timeout_us)
+{
+    errno = ENOSYS;
+
+    return BHT_ERROR;
+}
+
 #endif

+ 32 - 0
core/shared/platform/windows/win_socket.c

@@ -209,6 +209,38 @@ os_socket_addr_local(bh_socket_t socket, uint8_t *buf, size_t buflen,
     return BHT_ERROR;
 }
 
+int
+os_socket_set_send_timeout(bh_socket_t socket, uint64 timeout_us)
+{
+    errno = ENOSYS;
+
+    return BHT_ERROR;
+}
+
+int
+os_socket_get_send_timeout(bh_socket_t socket, uint64 *timeout_us)
+{
+    errno = ENOSYS;
+
+    return BHT_ERROR;
+}
+
+int
+os_socket_set_recv_timeout(bh_socket_t socket, uint64 timeout_us)
+{
+    errno = ENOSYS;
+
+    return BHT_ERROR;
+}
+
+int
+os_socket_get_recv_timeout(bh_socket_t socket, uint64 *timeout_us)
+{
+    errno = ENOSYS;
+
+    return BHT_ERROR;
+}
+
 int
 os_socket_addr_remote(bh_socket_t socket, uint8_t *buf, size_t buflen,
                       uint16_t *port, uint8_t *is_ipv4)

+ 3 - 0
samples/socket-api/CMakeLists.txt

@@ -90,6 +90,7 @@ ExternalProject_Add(wasm-app
                       tcp_client.wasm ${CMAKE_CURRENT_SOURCE_DIR}/build
                       tcp_server.wasm ${CMAKE_CURRENT_SOURCE_DIR}/build
                       send_recv.wasm ${CMAKE_CURRENT_SOURCE_DIR}/build
+                      socket_opts.wasm ${CMAKE_CURRENT_SOURCE_DIR}/build
 )
 
 add_executable(tcp_server ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/tcp_server.c)
@@ -102,6 +103,8 @@ target_link_libraries(send_recv pthread)
 
 add_executable(addr_resolve ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/addr_resolve.c)
 
+add_executable(socket_opts ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/socket_opts.c)
+
 ############################################
 ## Build iwasm with wasi and pthread support
 ############################################

+ 13 - 0
samples/socket-api/README.md

@@ -82,4 +82,17 @@ Data:
   And mourns for us
 ```
 
+`socket_opts.wasm` shows an example of getting and setting various supported socket options
+```bash
+$ ./iwasm ./socket_opts.wasm
+```
+
+The output is:
+```bash
+[Client] Create socket
+recv_timeout is expected
+send_timeout is expected
+[Client] Close socket
+```
+
 Refer to [socket api document](../../doc/socket_api.md) for more details.

+ 1 - 0
samples/socket-api/wasm-src/CMakeLists.txt

@@ -80,3 +80,4 @@ compile_with_clang(tcp_server.c)
 compile_with_clang(tcp_client.c)
 compile_with_clang(send_recv.c)
 compile_with_clang(addr_resolve.c)
+compile_with_clang(socket_opts.c)

+ 56 - 0
samples/socket-api/wasm-src/socket_opts.c

@@ -0,0 +1,56 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#ifdef __wasi__
+#include <wasi_socket_ext.h>
+#endif
+
+#define OPTION_ASSERT(A, B, OPTION)           \
+    if (A == B) {                             \
+        printf("%s is expected\n", OPTION);   \
+    }                                         \
+    else {                                    \
+        printf("%s is unexpected\n", OPTION); \
+        return EXIT_FAILURE;                  \
+    }
+
+struct timeval
+to_timeval(time_t tv_sec, suseconds_t tv_usec)
+{
+    struct timeval tv = { tv_sec, tv_usec };
+    return tv;
+}
+
+int
+main(int argc, char *argv[])
+{
+    int socket_fd = 0;
+    struct timeval tv;
+    socklen_t tv_len = sizeof(tv);
+
+    printf("[Client] Create socket\n");
+    socket_fd = socket(AF_INET, SOCK_STREAM, 0);
+    if (socket_fd == -1) {
+        perror("Create socket failed");
+        return EXIT_FAILURE;
+    }
+
+    tv = to_timeval(123, 1000);
+    setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+    tv = to_timeval(0, 0);
+    getsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, &tv_len);
+    OPTION_ASSERT(tv.tv_sec, 123, "SO_RCVTIMEO tv_sec");
+    OPTION_ASSERT(tv.tv_usec, 1000, "SO_RCVTIMEO tv_usec");
+
+    tv = to_timeval(456, 2000);
+    setsockopt(socket_fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
+    tv = to_timeval(0, 0);
+    getsockopt(socket_fd, SOL_SOCKET, SO_SNDTIMEO, &tv, &tv_len);
+    OPTION_ASSERT(tv.tv_sec, 456, "SO_SNDTIMEO tv_sec");
+    OPTION_ASSERT(tv.tv_usec, 2000, "SO_SNDTIMEO tv_usec");
+
+    printf("[Client] Close socket\n");
+    close(socket_fd);
+    return EXIT_SUCCESS;
+}