#include "mgsocket.h"
#include <stdlib.h>
#include <string.h>
#ifdef __EMSCRIPTEN__
#include "emscripten.h"
#include "mgwasm.h"
#endif
#define MG_RETRY_ON_EINTR(expression) \
__extension__({ \
long result; \
do { \
result = (long)(expression); \
} while (result == -1L && (errno == EINTR)); \
result; \
})
int mg_socket_init(void) { return MG_SUCCESS; }
int mg_socket_create(int af, int type, int protocol) {
int sockfd = socket(af, type, protocol);
if (sockfd == -1) {
return MG_ERROR_SOCKET;
}
return sockfd;
}
int mg_socket_create_handle_error(int sock, mg_session *session) {
if (sock == MG_ERROR_SOCKET) {
mg_session_set_error(session, "couldn't open socket: %s",
mg_socket_error());
return MG_ERROR_NETWORK_FAILURE;
}
return MG_SUCCESS;
}
static int mg_socket_test_status_is_error(long status) {
#ifdef __EMSCRIPTEN__
if (status == -1L && errno != EINPROGRESS) {
#else
if (status == -1L) {
#endif
return 1;
}
return 0;
}
int mg_socket_connect(int sock, const struct sockaddr *addr,
socklen_t addrlen) {
long status = MG_RETRY_ON_EINTR(connect(sock, addr, addrlen));
if (mg_socket_test_status_is_error(status)) {
return MG_ERROR_SOCKET;
}
#ifdef __EMSCRIPTEN__
if (mg_wasm_suspend_until_ready_to_write(sock) == -1) {
return MG_ERROR_SOCKET;
}
#endif
return MG_SUCCESS;
}
int mg_socket_connect_handle_error(int *sock, int status, mg_session *session) {
if (status != MG_SUCCESS) {
mg_session_set_error(session, "couldn't connect to host: %s",
mg_socket_error());
if (mg_socket_close(*sock) != 0) {
abort();
}
*sock = MG_ERROR_SOCKET;
return MG_ERROR_NETWORK_FAILURE;
}
return MG_SUCCESS;
}
int mg_socket_options(int sock, mg_session *session) {
#ifdef __EMSCRIPTEN__
(void)sock;
(void)session;
return MG_SUCCESS;
#else
struct {
int level;
int optname;
int optval;
} socket_options[] = { {SOL_TCP, TCP_NODELAY, 1},
{SOL_SOCKET, SO_KEEPALIVE, 1},
{SOL_TCP, TCP_KEEPIDLE, 20},
{SOL_TCP, TCP_KEEPCNT, 4},
{SOL_TCP, TCP_KEEPINTVL, 15}};
const size_t OPTCNT = sizeof(socket_options) / sizeof(socket_options[0]);
for (size_t i = 0; i < OPTCNT; ++i) {
int optval = socket_options[i].optval;
socklen_t optlen = sizeof(optval);
if (setsockopt(sock, socket_options[i].level, socket_options[i].optname,
(void *)&optval, optlen) != 0) {
mg_session_set_error(session, "couldn't set socket option: %s",
mg_socket_error());
if (mg_socket_close(sock) != 0) {
abort();
}
return MG_ERROR_NETWORK_FAILURE;
}
}
return MG_SUCCESS;
#endif
}
ssize_t mg_socket_send(int sock, const void *buf, int len) {
return MG_RETRY_ON_EINTR(send(sock, buf, len, MSG_NOSIGNAL));
}
ssize_t mg_socket_receive(int sock, void *buf, int len) {
return MG_RETRY_ON_EINTR(recv(sock, buf, len, 0));
}
int mg_socket_poll(struct pollfd *fds, unsigned int nfds, int timeout) {
return MG_RETRY_ON_EINTR(poll(fds, nfds, timeout));
}
int mg_socket_pair(int d, int type, int protocol, int *sv) {
return socketpair(d, type, protocol, sv);
}
int mg_socket_close(int sock) { return MG_RETRY_ON_EINTR(close(sock)); }
char *mg_socket_error(void) { return strerror(errno); }
void mg_socket_finalize(void) {}