From 6c49e2e93b23defd51d4b342baad5a9c3f843ea1 Mon Sep 17 00:00:00 2001 From: Trumeet Date: Sat, 23 Jul 2022 11:03:20 -0700 Subject: feat(libacron/acronc): delegate socket IO to the client API:CHANGE Signed-off-by: Trumeet --- client/libacron/CMakeLists.txt | 7 +- client/libacron/README.md | 69 ++++++++++-- client/libacron/acronc/main.c | 142 +++++++++++++++++------ client/libacron/acronc/net.c | 181 ++++++++++++++++++++++++++++++ client/libacron/acronc/net.h | 45 ++++++++ client/libacron/include/net.h | 51 ++++++++- client/libacron/net.c | 210 +++++++++-------------------------- client/libacron/private/connection.h | 6 - client/libacron/private/win32.h | 3 - 9 files changed, 499 insertions(+), 215 deletions(-) create mode 100644 client/libacron/acronc/net.c create mode 100644 client/libacron/acronc/net.h (limited to 'client/libacron') diff --git a/client/libacron/CMakeLists.txt b/client/libacron/CMakeLists.txt index 82ecc24..223b259 100644 --- a/client/libacron/CMakeLists.txt +++ b/client/libacron/CMakeLists.txt @@ -91,7 +91,12 @@ target_link_libraries(ac-static ${LIBAC_DEPS}) add_executable(acronc acronc/main.c - ) + acronc/net.c + acronc/net.h) +if(WIN32) + set(ACRONC_DEPS ws2_32) +endif() +set(ACRONC_DEPS ${ACRON_DEPS} ac) target_link_libraries(acronc ac) target_include_directories(acronc PUBLIC "${PROJECT_BINARY_DIR}" include/) diff --git a/client/libacron/README.md b/client/libacron/README.md index e5ed81b..09e36dc 100644 --- a/client/libacron/README.md +++ b/client/libacron/README.md @@ -164,6 +164,22 @@ pass this pointer as-is when making connections or disconnecting. Every thread can have multiple connections simultaneously. +In addition, the client is required to provide its own socket IO implementations due to: + +1. Cross-platform is easier. +2. It is easier for users to supply their own async IO mechanism. + +The client needs to establish a socket connection before calling ac_connect. It should also +supply the pointer to its socket identifier (anything is accepted: libac only needs a pointer) to +`ac_connection_parameters`. A typical type is `* int` (pointer to socket fd). + +The client also needs to supply its own `on_send` function. libac will call that function when it +needs to write anything to the socket. When calling that function, the socket pointer will be passed +as-is. + +No worries, `acronc/net.c` provides a ready-to-use example of cross-platform (Unix and Windows) blocking +socket client. See `acronc/main.c` for more details. + ```c void *connection = NULL; int r; @@ -172,40 +188,79 @@ ac_connection_parameters_t args = { .port = 25575, .version = 0, .id = "1", - .token = "123" + .token = "123", + .on_send = /* TODO: on_send callback function */, + .socket = NULL }; +int socket = /* TODO: Connect to the socket. */; +args.socket = &socket; if ((r = ac_connect(args, &connection))) { return r; } /* Listen or make requests. */ ``` -Then, the program can listen for responses or events: +Upon `ac_connect`, libac only does a little to initialize the connection (e. g. write initial +handshake request to the socket). The handshake is not yet completed, and the client cannot send +requests yet. To display the state, libac provides a function called `ac_get_state()` which outputs +the current state to the connection (`AC_STATE_INIT`, `AC_STATE_READY` or `AC_STATE_CLOSED`). +A common usage is to loop `ac_receive` (see below) infinitely after `ac_connect` until the state +changes from `AC_STATE_INIT` to `AC_STATE_READY`, where `ac_request` is allowd and valid data will +return from `ac_receive`. + +Note: if the state changed to `AC_STATE_CLOSED` within `ac_receive`, this is likely due to an abnormal +termination of the connection. In this case, `ac_receive` will return `AC_E_NET`. + +Then, the program can listen for responses or events. +The client should do its own magic to receive from socket and +supply `ac_receive` with a buffer and size. ```c ac_obj_t *obj = NULL; -while (!interrupted || !(r = ac_receive(connection, &obj))) { +uint8_t buffer[1000U]; +size_t bytes; +/* TODO: recv() */ +while (!(r = ac_receive(connection, buffer, bytes, &obj))) { /* The obj is now referencing to a library allocated event or response. */ /* Do things with the event or response. */ ac_obj_free(obj); } ``` -The program can make requests using `ac_request()`. ~~This function can -be called from any thread (not specific to the listening one), but any thread -calling this function must also call `ac_init()` first.~~ **On the roadmap.** +The program can make requests using `ac_request()`: + +```c +ac_config_t conf = { + .name = "acronc" +}; +ac_request_cmd_t req = { + .type = AC_REQUEST_CMD, + .id = 100, + .config = &conf, + .cmd = "say Hi" +}; +ac_request(connection, (ac_request_t *) &req)); +``` Finally, when done, call `ac_disconnect()`. It will close the socket and free the `connection` parameter. The `connection` pointer after the method call is undefined. +The client must manually close its socket after calling `ac_disconnect()`. > **Notes**: > -> Only `ws://` is supported at this time. `wss://` support is +> Only unencrypted WebSocket is supported at this time. `wss://` support is > on the roadmap. Read more: `acronc/main.c` example client. +#### Connection Threading + +libac reads stores some state in the connection object upon each function call. Therefore, +it is the caller's responsibility to guard the whole connection with a mutex. libac does not +provide that because this may limit the flexibility. After locking, it is safe to call the +connection-related functions from any threads. + ## Roadmap * Make unit tests diff --git a/client/libacron/acronc/main.c b/client/libacron/acronc/main.c index f76b172..43802ad 100644 --- a/client/libacron/acronc/main.c +++ b/client/libacron/acronc/main.c @@ -3,15 +3,16 @@ */ #include "libac.h" +#include "net.h" #include -#ifdef WIN32 -#include -#include -#else + +#ifndef WIN32 + #include #include #include + #endif static const char *world_name(const enum ac_world world) { @@ -126,11 +127,47 @@ static void handle_response(const ac_response_t *response) { } #ifndef WIN32 + static void intr(int signum) {} + #endif +static int on_send(const void *sock, + const void *buffer, + const size_t len) { + int r; + printf("Writing %lu bytes.\n", len); + if ((r = net_send(sock, buffer, len))) { + fprintf(stderr, "Cannot write %lu bytes to server: %s (%d).\n", + len, + net_strerror(r), + r); + } + return r; +} + +static int say(void *connection) { + int r; + ac_config_t conf = { + .name = "acronc" + }; + ac_request_cmd_t req = { + .type = AC_REQUEST_CMD, + .id = 100, + .config = &conf, + .cmd = "say Hi" + }; + if ((r = ac_request(connection, (ac_request_t *) &req))) { + return r; + } + return 0; +} + int main(int argc, char **argv) { int r; + if ((r = net_init())) { + return r; + } #ifdef WIN32 fprintf(stderr, "Warning: ^C handler on Windows is not yet available.\n"); #else @@ -139,27 +176,13 @@ int main(int argc, char **argv) { sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGINT, &sa, NULL) || - sigaction(SIGTERM, &sa, NULL)) { + sigaction(SIGTERM, &sa, NULL)) { const int e = errno; fprintf(stderr, "Cannot set SIGINT or SIGTERM handlers: %s (%d).\n", strerror(e), e); return e; } -#endif -#ifdef WIN32 - WORD wVersionRequested; - WSADATA wsaData; - wVersionRequested = MAKEWORD(2, 2); - if ((r = WSAStartup(wVersionRequested, &wsaData))) { - fprintf(stderr, "WSAStartup failed with error: %d\n", r); - return r; - } - if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { - fprintf(stderr, "Could not find a usable version of Winsock.dll\n"); - WSACleanup(); - return 1; - } #endif libac_config_t config = { #ifdef DEBUG @@ -171,33 +194,69 @@ int main(int argc, char **argv) { }; if ((r = ac_init(&config))) return r; void *connection; + SOCKET sock; ac_connection_parameters_t parameters = { .host = "localhost", .port = 25575, .id = "1", .token = "123", - .version = 0 + .version = 0, + .sock = NULL, + .on_send = on_send }; - if ((r = ac_connect(parameters, &connection))) { + if ((r = net_connect(parameters.host, parameters.port, &sock))) { + fprintf(stderr, "Cannot connect to server: %s (%d).\n", + net_strerror(r), + r); ac_free(); + net_free(); return r; } - ac_obj_t *obj; - ac_config_t conf = { - .name = "acronc" - }; - ac_request_cmd_t req = { - .type = AC_REQUEST_CMD, - .id = 100, - .config = &conf, - .cmd = "say Hi" - }; - if ((r = ac_request(connection, (ac_request_t *) &req))) { - ac_disconnect(connection); + parameters.sock = &sock; + if ((r = ac_connect(parameters, &connection))) { + net_close(&sock); ac_free(); + net_free(); return r; } - while (!(r = ac_receive(connection, &obj, 10))) { + ac_obj_t *obj; + int nr; + uint8_t buffer[1000U]; + size_t bytes; + enum ac_connection_state state; + printf("Waiting until the connection is established.\n"); + bool ready = false; + while (1) { + if ((nr = net_read(&sock, buffer, sizeof(buffer), &bytes, 10))) { + if (nr == NET_TIMEOUT) { + printf("Receive timeout.\n"); + continue; + } else { + goto end; + } + } + if ((r = ac_receive(connection, buffer, bytes, &obj))) { + goto end; + } + if (!ready) { + /* Wait until ready. */ + if ((r = ac_get_state(connection, &state))) { + goto end; + } + switch (state) { + case AC_STATE_INIT: + continue; + case AC_STATE_READY: + printf("Connection is established.\n"); + ready = true; + say(connection); + continue; + default: + fprintf(stderr, "Unexpected state.\n"); + goto end; + } + } + if (!obj) { continue; } @@ -208,10 +267,19 @@ int main(int argc, char **argv) { } ac_object_free(obj); } + end: + if (nr) { + if (nr == NET_CLOSED) { + fprintf(stderr, "Connection is closed while reading from the server.\n"); + } else { + fprintf(stderr, "Cannot receive from server: %s (%d).\n", + net_strerror(nr), + nr); + } + } ac_disconnect(connection); + net_close(&sock); ac_free(); -#ifdef WIN32 - WSACleanup(); -#endif + net_free(); return r; } \ No newline at end of file diff --git a/client/libacron/acronc/net.c b/client/libacron/acronc/net.c new file mode 100644 index 0000000..83192e0 --- /dev/null +++ b/client/libacron/acronc/net.c @@ -0,0 +1,181 @@ +/* + * Created by yuuta on 7/22/22. + * + * A simple cross-platform (Unix and Windows) implementation of a socket client. + */ + +#include "net.h" + +#include +#include +#include +#include + +#ifdef WIN32 +#include /* For DWORD */ +#include +#include + +#define errno_sock WSAGetLastError() + +#else + +#include +#include +#include +#include + +#define closesocket(socket) close(socket) +#define errno_sock errno + +#endif + +int net_connect(const char *host, + const uint16_t port, + SOCKET *sock) { + int r; + struct addrinfo *res; + char service[6]; + + snprintf(service, 6, "%u", port); + if ((r = getaddrinfo(host, service, NULL, &res))) { + return r; + } + + SOCKET fd; + if ((fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) <= 0) { + const int e = errno_sock; + freeaddrinfo(res); + return e; + } + if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) { + const int e = errno_sock; + closesocket(fd); + freeaddrinfo(res); + return e; + } + freeaddrinfo(res); + *sock = fd; + return 0; +} + +int net_read(const SOCKET *sock, + void *buffer, + const size_t length, + size_t *read, + const unsigned int timeout) { + +#ifdef WIN32 + DWORD to = timeout * 1000; + if (setsockopt(*sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&to, sizeof to)) { +#else + struct timeval tv; + tv.tv_sec = timeout; + tv.tv_usec = 0; + if (setsockopt(*sock, SOL_SOCKET, SO_RCVTIMEO, (const char *) &tv, sizeof tv)) { +#endif + return errno_sock; + } + + int bytes; + if ((bytes = (int) recv(*sock, buffer, length, 0)) <= 0) { + if (bytes < 0) { + const int e = errno_sock; +#ifdef WIN32 + if (e == WSAETIMEDOUT) { +#else + if (e == EAGAIN) { +#endif + return NET_TIMEOUT; + } + return e; + } + return NET_CLOSED; + } + *read = bytes; + return 0; +} + +int net_send(const SOCKET *sock, + const void *buffer, + size_t len) { + size_t pos; + const uint8_t *ptr = buffer; + int retval; + + for (pos = 0U; pos < len; pos += retval) { + size_t n = len - pos; + retval = (int) send(*sock, &ptr[pos], (int) n, 0); + if (retval <= 0) { + return errno_sock; + } + } + return 0; +} + +int net_init(void) { +#ifdef WIN32 + int r; + WORD wVersionRequested; + WSADATA wsaData; + wVersionRequested = MAKEWORD(2, 2); + if ((r = WSAStartup(wVersionRequested, &wsaData))) { + fprintf(stderr, "WSAStartup failed with error: %d\n", r); + return r; + } + if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { + fprintf(stderr, "Could not find a usable version of Winsock.dll\n"); + WSACleanup(); + return 1; + } +#endif + return 0; +} + +void net_close(const SOCKET *sock) { + closesocket(*sock); +} + +#ifdef WIN32 +static __declspec( thread ) LPTSTR err_buf = NULL; +#else +static _Thread_local char err_buf[1024]; +#endif + +void net_free(void) { +#ifdef WIN32 + WSACleanup(); + if (err_buf) { + LocalFree(err_buf); + err_buf = NULL; + } +#endif +} + +char *net_strerror(int errnum) { +#ifdef WIN32 + if (err_buf) { + LocalFree(err_buf); + err_buf = NULL; + } + FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + errnum, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &err_buf, + 0, + NULL); + if (!err_buf) { + return "Unknown error"; + } +#else + int r; + if ((r = strerror_r(errnum, err_buf, sizeof(err_buf) / sizeof(char)))) { + snprintf(err_buf, 1024, "%d (%d)", errnum, r); + } +#endif + return err_buf; +} \ No newline at end of file diff --git a/client/libacron/acronc/net.h b/client/libacron/acronc/net.h new file mode 100644 index 0000000..1a49709 --- /dev/null +++ b/client/libacron/acronc/net.h @@ -0,0 +1,45 @@ +/* + * Created by yuuta on 7/23/22. + */ + +#ifndef ACRONC_NET_H +#define ACRONC_NET_H + +#include +#include + +#ifdef WIN32 +#include + +#else + +#define SOCKET int + +#endif + +#define NET_CLOSED -2 +#define NET_TIMEOUT -1 + +int net_connect(const char *host, + uint16_t port, + SOCKET *sock); + +int net_read(const SOCKET *sock, + void *buffer, + size_t length, + size_t *read, + unsigned int timeout); + +int net_send(const SOCKET *sock, + const void *buffer, + size_t len); + +void net_close(const SOCKET *sock); + +int net_init(void); + +void net_free(void); + +char *net_strerror(int errnum); + +#endif /* ACRONC_NET_H */ diff --git a/client/libacron/include/net.h b/client/libacron/include/net.h index 5cf8f67..03be12f 100644 --- a/client/libacron/include/net.h +++ b/client/libacron/include/net.h @@ -10,6 +10,8 @@ #include "events.h" #include "requests.h" +#include + /* Connection */ typedef struct ac_connection_parameters { @@ -18,8 +20,38 @@ typedef struct ac_connection_parameters { unsigned int version; char *id; char *token; + /** + * An established socket using the host and port parameters above. + * The type of the value is opaque to libac, and it will pass + * the value as-is to the on_send function pointer parameter. + * + * The user must keep the socket to disconnect manuall after + * calling ac_disconnect(). + */ + void *sock; + + /** + * Called when ac_request needs to write to the socket. + * This function is called on the same thread calling ac_*. + * Any functions related to the connection may call on_send. + * These functions do not have internal locking, and concurrent + * accesses to the same connection object is a racing condition. + * Applications are required to do their own locking to guard + * the connection. + * @param sock The same sock passed in. + * @param buffer The buffer of data. + * @param len The length to the buffer. + * @return 0 on success. Otherwise error. + */ + int (*on_send)(const void *sock, const void *buffer, const size_t len); } ac_connection_parameters_t; +enum ac_connection_state { + AC_STATE_INIT, + AC_STATE_READY, + AC_STATE_CLOSED +}; + /** * Connect to the server. * @param parameters Connection parameters. Untouched. @@ -31,6 +63,7 @@ LIBAC_EXPORT int ac_connect(ac_connection_parameters_t parameters, void **out); /** * Disconnect the connection. + * The user must manually disconnect the socket passed in during ac_connect. * @param connection A non-NULL and connected connection passed as-is from ac_connect. * @return AC_E_OK or an error code. When failed, connection is undefined. When succeeds, connection is freed and invalid. * MT-Unsafe @@ -40,10 +73,24 @@ LIBAC_EXPORT int ac_disconnect(void *connection); /** * Blocks the current thread until a new response or event arrives. * @param connection A non-NULL and connected connection passed as-is from ac_connect. + * @param buffer Buffer of data read from the socket. + * @param len Length to buffer. * @param response Output response of either an event or a response. May be NULL even if it succeeds. - * @param timeout Read timeout in seconds. Set to 0 to wait infinitely. * @return AC_E_OK or an error code. When failed, *response is NULL. + * Notes: if the state is changed to CLOSED after receiving, this function will return AC_E_NET. + */ +LIBAC_EXPORT int ac_receive(void *connection, + const void *buffer, + size_t len, + ac_obj_t **response); + +/** + * Get the current state of the connection. + * @param connection A non-NULL and connected connection passed as-is from ac_connect. + * @param out Output state. + * @return AC_E_OK or an error code. When failed, *out is untouched. */ -LIBAC_EXPORT int ac_receive(void *connection, ac_obj_t **response, long timeout); +LIBAC_EXPORT int ac_get_state(void *connection, + enum ac_connection_state *out); #endif /* LIBAC_NET_H */ diff --git a/client/libacron/net.c b/client/libacron/net.c index bbfb465..1af752c 100644 --- a/client/libacron/net.c +++ b/client/libacron/net.c @@ -2,7 +2,6 @@ * Created by yuuta on 7/13/22. */ -#include "win32.h" #include "config.h" #include "net.h" #include "connection.h" @@ -15,24 +14,10 @@ #include #include -#include #include #include #include -#ifdef WIN32 -#include /* For DWORD */ -#include -#include -#define errno_sock WSAGetLastError() -#else -#define errno_sock errno -#include -#include -#include -#include -#endif - static void conn_free(struct ac_connection *conn) { if (conn->url) free(conn->url); if (conn->parameters.id) free(conn->parameters.id); @@ -41,33 +26,12 @@ static void conn_free(struct ac_connection *conn) { free(conn); } -static void on_open_handler(struct wic_inst *inst) { - struct ac_connection *conn = wic_get_app(inst); - conn->established = true; -} - static void on_send_handler(struct wic_inst *inst, const void *data, size_t size, enum wic_buffer type) { struct ac_connection *conn = wic_get_app(inst); - size_t pos; - const uint8_t *ptr = data; - int retval; - - for (pos = 0U; pos < size; pos += retval) { - size_t n = size - pos; - LOGDV("write(%d, %p[%u, %u])", conn->fd, data, pos, n); - retval = (int) send(conn->fd, &ptr[pos], (int) n, 0); - if (retval <= 0) { - /* There's no way to abort the process. */ - int e = errno_sock; - LOGEV("Cannot write to socket: %s (%d).", - strerror2(errno), - e); - break; - } - } + conn->parameters.on_send(conn->parameters.sock, data, size); free((void *) data); } @@ -128,7 +92,9 @@ int ac_connect(ac_connection_parameters_t parameters, void **out) { if (!parameters.id || !parameters.host || - !parameters.token) { + !parameters.token || + !parameters.sock || + !parameters.on_send) { LOGE("Required parameters are missing."); return AC_E_INVALID_REQUEST; } @@ -140,6 +106,9 @@ int ac_connect(ac_connection_parameters_t parameters, void **out) { } /* Do not use memcpy() here because we want to keep track of copied and not-copied strings. */ conn->parameters.port = parameters.port; + conn->parameters.sock = parameters.sock; + conn->parameters.version = parameters.version; + conn->parameters.on_send = parameters.on_send; if ((r = strdup2(parameters.token, &conn->parameters.token)) || (r = strdup2(parameters.host, &conn->parameters.host)) || (r = strdup2(parameters.id, &conn->parameters.id))) { @@ -183,7 +152,6 @@ int ac_connect(ac_connection_parameters_t parameters, void **out) { arg.rx_max = sizeof(rx); arg.on_send = on_send_handler; - arg.on_open = on_open_handler; arg.on_message = on_message_handler; arg.on_buffer = on_buffer_handler; @@ -197,82 +165,12 @@ int ac_connect(ac_connection_parameters_t parameters, void **out) { return AC_E_INTERNAL; } - struct addrinfo *res; - char service[6]; - - const enum wic_schema schema = wic_get_url_schema(inst); - const uint16_t port = wic_get_url_port(inst); - snprintf(service, 6, "%u", port); - const char *host = wic_get_url_hostname(inst); - - switch (schema) { - case WIC_SCHEMA_WS: - break; - case WIC_SCHEMA_WSS: - LOGE("WSS is not supported yet."); - conn_free(conn); - return AC_E_INVALID_REQUEST; - default: - LOGE("Unsupported protocol. The URL must be ws://"); - conn_free(conn); - return AC_E_INVALID_REQUEST; - } - - if ((r = getaddrinfo(host, service, NULL, &res))) { - /* gai_strerror() seems to cause an linkage error. */ -#ifdef WIN32 - LOGEV("Resolve host: %d.", r); -#else - LOGEV("Resolve host: %s.", gai_strerror(r)); -#endif - r = AC_E_NET; - conn_free(conn); - return r; - } - - SOCKET fd; - if ((fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) <= 0) { - const int e = errno_sock; - LOGEV("Cannot create socket: %s (%d).", - strerror2(e), - e); - freeaddrinfo(res); - conn_free(conn); - return AC_E_NET; - } - if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) { - const int e = errno_sock; - LOGEV("Cannot connect to socket: %s (%d).", - strerror2(e), - e); - closesocket(fd); - freeaddrinfo(res); - conn_free(conn); - return AC_E_NET; - } - freeaddrinfo(res); - - conn->fd = fd; if (wic_start(inst) != WIC_STATUS_SUCCESS) { LOGE("Cannot start the WIC client."); - closesocket(fd); conn_free(conn); return AC_E_NET; } - /* Wait until established (ready to send). */ - while (!conn->established) { - ac_obj_t *obj = NULL; - r = ac_receive(conn, &obj, 0); - if (obj) { - LOGW("Received an object before connection is established. Dropping."); - ac_object_free(obj); - } - if (r) { - ac_disconnect(conn); - return r; - } - } *out = conn; return AC_E_OK; } @@ -280,70 +178,27 @@ int ac_connect(ac_connection_parameters_t parameters, void **out) { int ac_disconnect(void *connection) { AC_CHECK_INIT; struct ac_connection *conn = connection; - if (!conn->fd) { - LOGE("Trying to disconnect an already disconnected connection."); - return AC_E_INVALID_REQUEST; - } LOGD("Disconnecting..."); wic_close(&conn->inst); - closesocket(conn->fd); conn_free(conn); return AC_E_OK; } -int ac_receive(void *connection, ac_obj_t **response, long timeout) { +int ac_receive(void *connection, + const void *buffer, + const size_t len, + ac_obj_t **response) { AC_CHECK_INIT; struct ac_connection *conn = connection; struct wic_inst *inst = &conn->inst; - static uint8_t buffer[1000U]; - int bytes; + const uint8_t *ptr = buffer; size_t retval, pos; -#ifdef WIN32 - DWORD to = timeout * 1000; - if (setsockopt(conn->fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&to, sizeof to)) { -#else - struct timeval tv; - tv.tv_sec = timeout; - tv.tv_usec = 0; - if (setsockopt(conn->fd, SOL_SOCKET, SO_RCVTIMEO, (const char *) &tv, sizeof tv)) { -#endif - const int e = errno_sock; - LOGEV("Failed to call setsockopt(3): %s (%d).", - strerror2(e), - e); - return AC_E_INTERNAL; - } - - if ((bytes = (int) recv(conn->fd, buffer, sizeof(buffer), 0)) <= 0) { - LOGDV("recv(%d) = %d", conn->fd, bytes); - if (bytes < 0) { - const int e = errno_sock; -#ifdef WIN32 - if (e == WSAETIMEDOUT) { -#else - if (e == EAGAIN) { -#endif - /* Timeout */ - *response = NULL; - return AC_E_OK; - } - LOGEV("Failed to receive from socket : %s (%d).", - strerror2(e), - e); - } else { - LOGI("Peer abnormally shutdown."); - } - wic_close_with_reason(inst, WIC_CLOSE_ABNORMAL_2, NULL, 0); - return AC_E_NET; - } - LOGDV("recv(%d) = %d", conn->fd, bytes); - /* In case the deserializer does not run at all. */ memset(&conn->result, 0, sizeof(struct ac_result)); - for (pos = 0U; pos < bytes; pos += retval) { - retval = wic_parse(inst, &buffer[pos], bytes - pos); + for (pos = 0U; pos < len; pos += retval) { + retval = wic_parse(inst, &ptr[pos], len - pos); if (wic_get_state(inst) == WIC_STATE_CLOSED) { LOGE("Connection closed."); return AC_E_NET; @@ -390,6 +245,17 @@ int ac_request(void *connection, const ac_request_t *request) { LOGE("Cannot serialize JSON."); return AC_E_INTERNAL; } + /** + * Access: + * * inst->role (ro, safe) + * * inst->state (rw, unsafe) + * * inst->utf8_tx (rw, safe) + * * inst->frag (rw, safe) + * * inst->on_buffer (ro, unsafe) + * * inst->on_sand (ro, unsafe) + * * conn->fd (ro, unsafe) + * * write(ro, unsafe) + */ if (wic_send_text(inst, true, str, len) != WIC_STATUS_SUCCESS) { LOGE("Cannot send."); json_object_put(obj); @@ -398,3 +264,29 @@ int ac_request(void *connection, const ac_request_t *request) { json_object_put(obj); return AC_E_OK; } + +int ac_get_state(void *connection, enum ac_connection_state *out) { + AC_CHECK_INIT; + struct ac_connection *conn = connection; + switch (wic_get_state(&conn->inst)) { + case WIC_STATE_CLOSED: { + *out = AC_STATE_CLOSED; + break; + } + case WIC_STATE_PARSE_HANDSHAKE: + case WIC_STATE_INIT: { + *out = AC_STATE_INIT; + break; + } + case WIC_STATE_OPEN: + case WIC_STATE_READY: { + *out = AC_STATE_READY; + break; + } + default: { + *out = AC_STATE_INIT; + break; + } + } + return AC_E_OK; +} diff --git a/client/libacron/private/connection.h b/client/libacron/private/connection.h index a76fada..a2bb62f 100644 --- a/client/libacron/private/connection.h +++ b/client/libacron/private/connection.h @@ -10,10 +10,6 @@ #include "libac.h" #include -#ifdef WIN32 -#include -#endif - /** * Used to transfer deserialization result from receive handler to ac_receive(). */ @@ -30,8 +26,6 @@ struct ac_connection { ac_connection_parameters_t parameters; struct wic_inst inst; char *url; - SOCKET fd; - bool established; /* Result from message handler. * Message handling will happen at the same thread as ac_receive (lib wic). */ struct ac_result result; diff --git a/client/libacron/private/win32.h b/client/libacron/private/win32.h index 07cdca8..a907b28 100644 --- a/client/libacron/private/win32.h +++ b/client/libacron/private/win32.h @@ -7,9 +7,6 @@ #ifdef WIN32 #define _Thread_local __declspec( thread ) -#else -#define SOCKET int -#define closesocket(socket) close(socket) #endif #endif /* LIBAC_WIN32_H */ -- cgit v1.2.3