#ifdef WIN32
#include <tchar.h>
#include <io.h>
+#include <mstcpip.h>
#else
#ifdef LWS_BUILTIN_GETIFADDRS
#include <getifaddrs.h>
#else
#include <ifaddrs.h>
#endif
+#include <syslog.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <netdb.h>
#endif
#endif
+#ifndef LWS_BUILD_HASH
+#define LWS_BUILD_HASH "unknown-build-hash"
+#endif
+
static int log_level = LLL_ERR | LLL_WARN | LLL_NOTICE;
-static void lwsl_emit_stderr(const char *line);
-static void (*lwsl_emit)(const char *line) = lwsl_emit_stderr;
-static const char *log_level_names[] = {
+static void lwsl_emit_stderr(int level, const char *line);
+static void (*lwsl_emit)(int level, const char *line) = lwsl_emit_stderr;
+
+static const char *library_version = LWS_LIBRARY_VERSION " " LWS_BUILD_HASH;
+
+static const char * const log_level_names[] = {
"ERR",
"WARN",
"NOTICE",
"HEADER",
"EXTENSION",
"CLIENT",
+ "LATENCY",
};
+#ifndef LWS_NO_CLIENT
+ extern int lws_client_socket_service(
+ struct libwebsocket_context *context,
+ struct libwebsocket *wsi, struct pollfd *pollfd);
+#endif
+#ifndef LWS_NO_SERVER
+ extern int lws_server_socket_service(
+ struct libwebsocket_context *context,
+ struct libwebsocket *wsi, struct pollfd *pollfd);
+#endif
+
+/**
+ * lws_get_library_version: get version and git hash library built from
+ *
+ * returns a const char * to a string like "1.1 178d78c"
+ * representing the library version followed by the git head hash it
+ * was built from
+ */
+
+const char *
+lws_get_library_version(void)
+{
+ return library_version;
+}
+
int
-insert_wsi_socket_into_fds(struct libwebsocket_context *context, struct libwebsocket *wsi)
+insert_wsi_socket_into_fds(struct libwebsocket_context *context,
+ struct libwebsocket *wsi)
{
if (context->fds_count >= context->max_fds) {
- lwsl_err("Reached limit of fds tracking (%d)\n", context->max_fds);
+ lwsl_err("Too many fds (%d)\n", context->max_fds);
return 1;
}
if (wsi->sock > context->max_fds) {
- lwsl_err("Socket fd %d is beyond what we can index (%d)\n", wsi->sock, context->max_fds);
+ lwsl_err("Socket fd %d is too high (%d)\n",
+ wsi->sock, context->max_fds);
return 1;
}
assert(wsi);
assert(wsi->sock);
- lwsl_info("insert_wsi_socket_into_fds: wsi=%p, sock=%d, fds pos=%d\n", wsi, wsi->sock, context->fds_count);
+ lwsl_info("insert_wsi_socket_into_fds: wsi=%p, sock=%d, fds pos=%d\n",
+ wsi, wsi->sock, context->fds_count);
context->lws_lookup[wsi->sock] = wsi;
wsi->position_in_fds_table = context->fds_count;
/* external POLL support via protocol 0 */
context->protocols[0].callback(context, wsi,
LWS_CALLBACK_ADD_POLL_FD,
- (void *)(long)wsi->sock, NULL, POLLIN);
+ wsi->user_space, (void *)(long)wsi->sock, POLLIN);
return 0;
}
static int
-remove_wsi_socket_from_fds(struct libwebsocket_context *context, struct libwebsocket *wsi)
+remove_wsi_socket_from_fds(struct libwebsocket_context *context,
+ struct libwebsocket *wsi)
{
int m;
goto do_ext;
if (wsi->sock > context->max_fds) {
- lwsl_err("Socket fd %d is beyond what we can index (%d)\n", wsi->sock, context->max_fds);
+ lwsl_err("Socket fd %d too high (%d)\n",
+ wsi->sock, context->max_fds);
return 1;
}
- lwsl_info("remove_wsi_socket_from_fds: wsi=%p, sock=%d, fds pos=%d\n", wsi, wsi->sock, wsi->position_in_fds_table);
+ lwsl_info("remove_wsi_socket_from_fds: wsi=%p, sock=%d, fds pos=%d\n",
+ wsi, wsi->sock, wsi->position_in_fds_table);
m = wsi->position_in_fds_table; /* replace the contents for this */
/* have the last guy take up the vacant slot */
- context->fds[m] = context->fds[context->fds_count]; /* vacant fds slot filled with end one */
- /* end guy's fds_lookup entry remains unchanged (still same fd pointing to same wsi) */
+ context->fds[m] = context->fds[context->fds_count];
+ /*
+ * end guy's fds_lookup entry remains unchanged
+ * (still same fd pointing to same wsi)
+ */
/* end guy's "position in fds table" changed */
- context->lws_lookup[context->fds[context->fds_count].fd]->position_in_fds_table = m;
+ context->lws_lookup[context->fds[context->fds_count].fd]->
+ position_in_fds_table = m;
/* deletion guy's lws_lookup entry needs nuking */
- context->lws_lookup[wsi->sock] = NULL; /* no WSI for the socket of the wsi being removed*/
- wsi->position_in_fds_table = -1; /* removed wsi has no position any more */
+ context->lws_lookup[wsi->sock] = NULL;
+ /* removed wsi has no position any more */
+ wsi->position_in_fds_table = -1;
do_ext:
/* remove also from external POLL support via protocol 0 */
if (wsi->sock)
context->protocols[0].callback(context, wsi,
- LWS_CALLBACK_DEL_POLL_FD, (void *)(long)wsi->sock, NULL, 0);
+ LWS_CALLBACK_DEL_POLL_FD, wsi->user_space,
+ (void *)(long)wsi->sock, 0);
return 0;
}
int old_state;
unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 2 +
LWS_SEND_BUFFER_POST_PADDING];
+#ifndef LWS_NO_EXTENSIONS
int ret;
int m;
struct lws_tokens eff_buf;
struct libwebsocket_extension *ext;
+#endif
if (!wsi)
return;
if (old_state == WSI_STATE_DEAD_SOCKET)
return;
- wsi->close_reason = reason;
+ /* we tried the polite way... */
+ if (old_state == WSI_STATE_AWAITING_CLOSE_ACK)
+ goto just_kill_connection;
+
+ wsi->u.ws.close_reason = reason;
+
+ if (wsi->mode == LWS_CONNMODE_HTTP_SERVING && wsi->u.http.fd) {
+ close(wsi->u.http.fd);
+ wsi->u.http.fd = 0;
+ }
+#ifndef LWS_NO_EXTENSIONS
/*
* are his extensions okay with him closing? Eg he might be a mux
* parent and just his ch1 aspect is closing?
*/
-
for (n = 0; n < wsi->count_active_extensions; n++) {
if (!wsi->active_extensions[n]->callback)
continue;
}
}
-
-
/*
* flush any tx pending from extensions, since we may send close packet
* if there are problems with send, just nuke the connection
if (eff_buf.token_len)
if (lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
- eff_buf.token_len)) {
- lwsl_debug("close: sending final extension spill had problems\n");
+ eff_buf.token_len) != eff_buf.token_len) {
+ lwsl_debug("close: ext spill failed\n");
goto just_kill_connection;
}
}
+#endif
/*
* signal we are closing, libsocket_write will
lwsl_debug("sending close indication...\n");
- n = libwebsocket_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING],
+ /* make valgrind happy */
+ memset(buf, 0, sizeof(buf));
+ n = libwebsocket_write(wsi,
+ &buf[LWS_SEND_BUFFER_PRE_PADDING + 2],
0, LWS_WRITE_CLOSE);
- if (!n) {
+ if (n >= 0) {
/*
* we have sent a nice protocol level indication we
* now wish to close, we should not send anything more
wsi->state = WSI_STATE_AWAITING_CLOSE_ACK;
- /* and we should wait for a reply for a bit out of politeness */
+ /*
+ * ...and we should wait for a reply for a bit
+ * out of politeness
+ */
libwebsocket_set_timeout(wsi,
PENDING_TIMEOUT_CLOSE_ACK, 1);
return;
}
- lwsl_info("close: sending the close packet failed, hanging up\n");
+ lwsl_info("close: sending close packet failed, hanging up\n");
/* else, the send failed and we should just hang up */
}
just_kill_connection:
- lwsl_debug("libwebsocket_close_and_free_session: just_kill_connection\n");
+ lwsl_debug("close: just_kill_connection\n");
/*
* we won't be servicing or receiving anything further from this guy
wsi->state = WSI_STATE_DEAD_SOCKET;
+ if ((old_state == WSI_STATE_ESTABLISHED ||
+ wsi->mode == LWS_CONNMODE_WS_SERVING ||
+ wsi->mode == LWS_CONNMODE_WS_CLIENT)) {
+
+ if (wsi->u.ws.rx_user_buffer) {
+ free(wsi->u.ws.rx_user_buffer);
+ wsi->u.ws.rx_user_buffer = NULL;
+ }
+ if (wsi->u.ws.rxflow_buffer) {
+ free(wsi->u.ws.rxflow_buffer);
+ wsi->u.ws.rxflow_buffer = NULL;
+ }
+ }
+
/* tell the user it's all over for this guy */
if (wsi->protocol && wsi->protocol->callback &&
wsi->protocol->callback(context, wsi, LWS_CALLBACK_CLOSED,
wsi->user_space, NULL, 0);
} else
- lwsl_debug("not calling back closed, old_state=%d\n", old_state);
+ lwsl_debug("not calling back closed\n");
+#ifndef LWS_NO_EXTENSIONS
/* deallocate any active extension contexts */
for (n = 0; n < wsi->count_active_extensions; n++) {
NULL, NULL, 0);
ext++;
}
-
- /* free up his parsing allocations */
-
- for (n = 0; n < WSI_TOKEN_COUNT; n++)
- if (wsi->utf8_token[n].token)
- free(wsi->utf8_token[n].token);
-#ifndef LWS_NO_CLIENT
- if (wsi->c_address)
- free(wsi->c_address);
#endif
- if (wsi->rxflow_buffer)
- free(wsi->rxflow_buffer);
/* lwsl_info("closing fd=%d\n", wsi->sock); */
if (wsi->sock) {
n = shutdown(wsi->sock, SHUT_RDWR);
if (n)
- lwsl_debug("closing: shutdown returned %d\n", errno);
+ lwsl_debug("closing: shutdown returned %d\n",
+ errno);
n = compatible_close(wsi->sock);
if (n)
- lwsl_debug("closing: close returned %d\n", errno);
+ lwsl_debug("closing: close returned %d\n",
+ errno);
}
#ifdef LWS_OPENSSL_SUPPORT
}
#endif
- if (wsi->protocol && wsi->protocol->per_session_data_size && wsi->user_space) /* user code may own */
+ if (wsi->protocol && wsi->protocol->per_session_data_size &&
+ wsi->user_space) /* user code may own */
free(wsi->user_space);
free(wsi);
}
/**
- * libwebsockets_hangup_on_client() - Server calls to terminate client
- * connection
- * @context: libwebsockets context
- * @fd: Connection socket descriptor
- */
-
-void
-libwebsockets_hangup_on_client(struct libwebsocket_context *context, int fd)
-{
- struct libwebsocket *wsi = context->lws_lookup[fd];
-
- if (wsi) {
- libwebsocket_close_and_free_session(context,
- wsi, LWS_CLOSE_STATUS_NOSTATUS);
- } else
- close(fd);
-}
-
-
-/**
* libwebsockets_get_peer_addresses() - Get client address information
+ * @context: Libwebsockets context
+ * @wsi: Local struct libwebsocket associated with
* @fd: Connection socket descriptor
* @name: Buffer to take client address name
* @name_len: Length of client address name buffer
*/
void
-libwebsockets_get_peer_addresses(int fd, char *name, int name_len,
+libwebsockets_get_peer_addresses(struct libwebsocket_context *context,
+ struct libwebsocket *wsi, int fd, char *name, int name_len,
char *rip, int rip_len)
{
unsigned int len;
char ip[128];
unsigned char *p;
int n;
+ int ret = -1;
#ifdef AF_LOCAL
- struct sockaddr_un *un;
+ struct sockaddr_un *un;
#endif
rip[0] = '\0';
name[0] = '\0';
- len = sizeof sin;
+ lws_latency_pre(context, wsi);
+
+ len = sizeof(sin);
if (getpeername(fd, (struct sockaddr *) &sin, &len) < 0) {
perror("getpeername");
- return;
+ goto bail;
}
- host = gethostbyaddr((char *) &sin.sin_addr, sizeof sin.sin_addr,
+ host = gethostbyaddr((char *) &sin.sin_addr, sizeof(sin.sin_addr),
AF_INET);
if (host == NULL) {
perror("gethostbyaddr");
- return;
+ goto bail;
}
strncpy(name, host->h_name, name_len);
host1 = gethostbyname(host->h_name);
if (host1 == NULL)
- return;
+ goto bail;
p = (unsigned char *)host1;
n = 0;
while (p != NULL) {
strncpy(rip, ip, rip_len);
rip[rip_len - 1] = '\0';
}
+
+ ret = 0;
+bail:
+ lws_latency(context, wsi, "libwebsockets_get_peer_addresses", ret, 1);
}
int libwebsockets_get_random(struct libwebsocket_context *context,
return n;
}
-unsigned char *
-libwebsockets_SHA1(const unsigned char *d, size_t n, unsigned char *md)
+int lws_set_socket_options(struct libwebsocket_context *context, int fd)
{
- return SHA1(d, n, md);
+ int optval = 1;
+ socklen_t optlen = sizeof(optval);
+#ifdef WIN32
+ unsigned long optl = 0;
+#endif
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
+ struct protoent *tcp_proto;
+#endif
+
+ if (context->ka_time) {
+ /* enable keepalive on this socket */
+ optval = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
+ (const void *)&optval, optlen) < 0)
+ return 1;
+
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
+
+ /*
+ * didn't find a way to set these per-socket, need to
+ * tune kernel systemwide values
+ */
+#elif WIN32
+ {
+ DWORD dwBytesRet;
+ struct tcp_keepalive alive;
+ alive.onoff = TRUE;
+ alive.keepalivetime = context->ka_time;
+ alive.keepaliveinterval = context->ka_interval;
+
+ if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive),
+ NULL, 0, &dwBytesRet, NULL, NULL))
+ return 1;
+ }
+#else
+ /* set the keepalive conditions we want on it too */
+ optval = context->ka_time;
+ if (setsockopt(fd, IPPROTO_IP, TCP_KEEPIDLE,
+ (const void *)&optval, optlen) < 0)
+ return 1;
+
+ optval = context->ka_interval;
+ if (setsockopt(fd, IPPROTO_IP, TCP_KEEPINTVL,
+ (const void *)&optval, optlen) < 0)
+ return 1;
+
+ optval = context->ka_probes;
+ if (setsockopt(fd, IPPROTO_IP, TCP_KEEPCNT,
+ (const void *)&optval, optlen) < 0)
+ return 1;
+#endif
+ }
+
+ /* Disable Nagle */
+ optval = 1;
+#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__NetBSD__)
+ setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen);
+#else
+ tcp_proto = getprotobyname("TCP");
+ setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, &optval, optlen);
+#endif
+
+ /* We are nonblocking... */
+#ifdef WIN32
+ ioctlsocket(fd, FIONBIO, &optl);
+#else
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+#endif
+
+ return 0;
}
int lws_send_pipe_choked(struct libwebsocket *wsi)
lws_handle_POLLOUT_event(struct libwebsocket_context *context,
struct libwebsocket *wsi, struct pollfd *pollfd)
{
- struct lws_tokens eff_buf;
int n;
+
+#ifndef LWS_NO_EXTENSIONS
+ struct lws_tokens eff_buf;
int ret;
int m;
int handled = 0;
/* assuming they gave us something to send, send it */
if (eff_buf.token_len) {
- if (lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
- eff_buf.token_len))
+ n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
+ eff_buf.token_len);
+ if (n < 0)
return -1;
+ /*
+ * Keep amount spilled small to minimize chance of this
+ */
+ if (n != eff_buf.token_len) {
+ lwsl_err("Unable to spill ext %d vs %s\n",
+ eff_buf.token_len, n);
+ return -1;
+ }
} else
continue;
wsi->extension_data_pending = 0;
user_service:
+#endif
/* one shot */
if (pollfd) {
/* external POLL support via protocol 0 */
context->protocols[0].callback(context, wsi,
LWS_CALLBACK_CLEAR_MODE_POLL_FD,
- (void *)(long)wsi->sock, NULL, POLLOUT);
+ wsi->user_space, (void *)(long)wsi->sock, POLLOUT);
}
-
+#ifndef LWS_NO_EXTENSIONS
notify_action:
+#endif
if (wsi->mode == LWS_CONNMODE_WS_CLIENT)
n = LWS_CALLBACK_CLIENT_WRITEABLE;
else
n = LWS_CALLBACK_SERVER_WRITEABLE;
- user_callback_handle_rxflow(wsi->protocol->callback, context,
- wsi, (enum libwebsocket_callback_reasons) n, wsi->user_space, NULL, 0);
-
- return 0;
+ return user_callback_handle_rxflow(wsi->protocol->callback, context,
+ wsi, (enum libwebsocket_callback_reasons) n,
+ wsi->user_space, NULL, 0);
}
-void
+int
libwebsocket_service_timeout_check(struct libwebsocket_context *context,
struct libwebsocket *wsi, unsigned int sec)
{
+#ifndef LWS_NO_EXTENSIONS
int n;
/*
wsi, LWS_EXT_CALLBACK_1HZ,
wsi->active_extensions_user[n], NULL, sec);
+#endif
if (!wsi->pending_timeout)
- return;
+ return 0;
/*
* if we went beyond the allowed time, kill the
lwsl_info("TIMEDOUT WAITING\n");
libwebsocket_close_and_free_session(context,
wsi, LWS_CLOSE_STATUS_NOSTATUS);
+ return 1;
}
+
+ return 0;
}
/**
* @pollfd: The pollfd entry describing the socket fd and which events
* happened.
*
- * This function closes any active connections and then frees the
- * context. After calling this, any further use of the context is
- * undefined.
+ * This function takes a pollfd that has POLLIN or POLLOUT activity and
+ * services it according to the state of the associated
+ * struct libwebsocket.
+ *
+ * The one call deals with all "service" that might happen on a socket
+ * including listen accepts, http files as well as websocket protocol.
*/
int
struct pollfd *pollfd)
{
struct libwebsocket *wsi;
- unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 1 +
- MAX_BROADCAST_PAYLOAD + LWS_SEND_BUFFER_POST_PADDING];
int n;
int m;
+ int listen_socket_fds_index = 0;
struct timeval tv;
+ int timed_out = 0;
+ int our_fd = 0;
+
+#ifndef LWS_NO_EXTENSIONS
int more = 1;
- struct lws_tokens eff_buf;
-#ifndef LWS_NO_CLIENT
- extern int lws_client_socket_service(struct libwebsocket_context *context, struct libwebsocket *wsi, struct pollfd *pollfd);
-#endif
-#ifndef LWS_NO_SERVER
- extern int lws_server_socket_service(struct libwebsocket_context *context, struct libwebsocket *wsi, struct pollfd *pollfd);
#endif
+ struct lws_tokens eff_buf;
+
+ if (context->listen_service_fd)
+ listen_socket_fds_index = context->lws_lookup[
+ context->listen_service_fd]->position_in_fds_table;
+
/*
* you can call us with pollfd = NULL to just allow the once-per-second
* global timeout checks; if less than a second since the last check
if (context->last_timeout_check_s != tv.tv_sec) {
context->last_timeout_check_s = tv.tv_sec;
+ #ifndef WIN32
+ /* if our parent went down, don't linger around */
+ if (context->started_with_parent &&
+ kill(context->started_with_parent, 0) < 0)
+ kill(getpid(), SIGTERM);
+ #endif
+
/* global timeout check once per second */
+ if (pollfd)
+ our_fd = pollfd->fd;
+
for (n = 0; n < context->fds_count; n++) {
- struct libwebsocket *new_wsi = context->lws_lookup[context->fds[n].fd];
- if (!new_wsi)
+ m = context->fds[n].fd;
+ wsi = context->lws_lookup[m];
+ if (!wsi)
continue;
- libwebsocket_service_timeout_check(context,
- new_wsi, tv.tv_sec);
+
+ if (libwebsocket_service_timeout_check(context, wsi,
+ tv.tv_sec))
+ /* he did time out... */
+ if (m == our_fd)
+ /* it was the guy we came to service! */
+ timed_out = 1;
}
}
+ /* the socket we came to service timed out, nothing to do */
+ if (timed_out)
+ return 0;
+
/* just here for timeout management? */
if (pollfd == NULL)
* pending connection here, it causes us to check again next time.
*/
- if (context->listen_service_fd && pollfd->fd != context->listen_service_fd) {
+ if (context->listen_service_fd && pollfd !=
+ &context->fds[listen_socket_fds_index]) {
context->listen_service_count++;
if (context->listen_service_extraseen ||
- context->listen_service_count == context->listen_service_modulo) {
+ context->listen_service_count ==
+ context->listen_service_modulo) {
context->listen_service_count = 0;
m = 1;
if (context->listen_service_extraseen > 5)
m = 2;
while (m--) {
- /* even with extpoll, we prepared this internal fds for listen */
- n = poll(&context->fds[0], 1, 0);
- if (n > 0) { /* there's a connection waiting for us */
- libwebsocket_service_fd(context, &context->fds[0]);
+ /*
+ * even with extpoll, we prepared this
+ * internal fds for listen
+ */
+ n = poll(&context->fds[listen_socket_fds_index],
+ 1, 0);
+ if (n > 0) { /* there's a conn waiting for us */
+ libwebsocket_service_fd(context,
+ &context->
+ fds[listen_socket_fds_index]);
context->listen_service_extraseen++;
} else {
if (context->listen_service_extraseen)
- context->listen_service_extraseen--;
+ context->
+ listen_service_extraseen--;
break;
}
}
wsi = context->lws_lookup[pollfd->fd];
if (wsi == NULL) {
if (pollfd->fd > 11)
- lwsl_err("unexpected NULL wsi fd=%d fds_count=%d\n", pollfd->fd, context->fds_count);
+ lwsl_err("unexpected NULL wsi fd=%d fds_count=%d\n",
+ pollfd->fd, context->fds_count);
return 0;
}
#ifndef LWS_NO_SERVER
case LWS_CONNMODE_HTTP_SERVING:
case LWS_CONNMODE_SERVER_LISTENER:
- case LWS_CONNMODE_BROADCAST_PROXY_LISTENER:
- case LWS_CONNMODE_BROADCAST_PROXY:
+ case LWS_CONNMODE_SSL_ACK_PENDING:
return lws_server_socket_service(context, wsi, pollfd);
#endif
/* handle session socket closed */
- if (pollfd->revents & (POLLERR | POLLHUP)) {
+ if ((!pollfd->revents & POLLIN) &&
+ (pollfd->revents & (POLLERR | POLLHUP))) {
lwsl_debug("Session Socket %p (fd=%d) dead\n",
(void *)wsi, pollfd->fd);
wsi->state == WSI_STATE_ESTABLISHED)
if (lws_handle_POLLOUT_event(context, wsi,
pollfd) < 0) {
+ lwsl_info("libwebsocket_service_fd: closing\n");
libwebsocket_close_and_free_session(
- context, wsi, LWS_CLOSE_STATUS_NORMAL);
+ context, wsi, LWS_CLOSE_STATUS_NOSTATUS);
return 0;
}
#ifdef LWS_OPENSSL_SUPPORT
read_pending:
- if (wsi->ssl)
- eff_buf.token_len = SSL_read(wsi->ssl, buf, sizeof buf);
- else
+ if (wsi->ssl) {
+ eff_buf.token_len = SSL_read(wsi->ssl,
+ context->service_buffer,
+ sizeof(context->service_buffer));
+ if (!eff_buf.token_len) {
+ n = SSL_get_error(wsi->ssl, eff_buf.token_len);
+ lwsl_err("SSL_read returned 0 with reason %s\n",
+ ERR_error_string(n,
+ (char *)context->service_buffer));
+ }
+ } else
#endif
- eff_buf.token_len =
- recv(pollfd->fd, buf, sizeof buf, 0);
+ eff_buf.token_len = recv(pollfd->fd,
+ context->service_buffer,
+ sizeof(context->service_buffer), 0);
if (eff_buf.token_len < 0) {
lwsl_debug("Socket read returned %d\n",
return 0;
}
if (!eff_buf.token_len) {
+ lwsl_info("closing connection due to 0 length read\n");
libwebsocket_close_and_free_session(context, wsi,
LWS_CLOSE_STATUS_NOSTATUS);
return 0;
* used then so it is efficient.
*/
- eff_buf.token = (char *)buf;
-
+ eff_buf.token = (char *)context->service_buffer;
+#ifndef LWS_NO_EXTENSIONS
more = 1;
while (more) {
if (m)
more = 1;
}
-
+#endif
/* service incoming data */
if (eff_buf.token_len) {
/* we closed wsi */
return 0;
}
-
+#ifndef LWS_NO_EXTENSIONS
eff_buf.token = NULL;
eff_buf.token_len = 0;
}
+#endif
#ifdef LWS_OPENSSL_SUPPORT
if (wsi->ssl && SSL_pending(wsi->ssl))
void
libwebsocket_context_destroy(struct libwebsocket_context *context)
{
+#ifndef LWS_NO_EXTENSIONS
int n;
int m;
struct libwebsocket_extension *ext;
+ struct libwebsocket_protocols *protocol = context->protocols;
+
+#ifdef LWS_LATENCY
+ if (context->worst_latency_info[0])
+ lwsl_notice("Worst latency: %s\n", context->worst_latency_info);
+#endif
for (n = 0; n < context->fds_count; n++) {
- struct libwebsocket *wsi = context->lws_lookup[context->fds[n].fd];
+ struct libwebsocket *wsi =
+ context->lws_lookup[context->fds[n].fd];
libwebsocket_close_and_free_session(context,
- wsi, LWS_CLOSE_STATUS_GOINGAWAY);
+ wsi, LWS_CLOSE_STATUS_NOSTATUS /* no protocol close */);
+ n--;
}
/*
if (context->listen_port)
m = LWS_EXT_CALLBACK_SERVER_CONTEXT_DESTRUCT;
while (ext && ext->callback) {
- ext->callback(context, ext, NULL, (enum libwebsocket_extension_callback_reasons)m, NULL, NULL, 0);
+ ext->callback(context, ext, NULL,
+ (enum libwebsocket_extension_callback_reasons)m,
+ NULL, NULL, 0);
ext++;
}
+ /*
+ * inform all the protocols that they are done and will have no more
+ * callbacks
+ */
+
+ while (protocol->callback) {
+ protocol->callback(context, NULL, LWS_CALLBACK_PROTOCOL_DESTROY,
+ NULL, NULL, 0);
+ protocol++;
+ }
+
+#endif
+
#ifdef WIN32
#else
close(context->fd_random);
SSL_CTX_free(context->ssl_ctx);
if (context->ssl_client_ctx)
SSL_CTX_free(context->ssl_client_ctx);
+
+ ERR_remove_state(0);
+ ERR_free_strings();
+ EVP_cleanup();
+ CRYPTO_cleanup_all_ex_data();
#endif
+ if (context->fds)
+ free(context->fds);
+ if (context->lws_lookup)
+ free(context->lws_lookup);
+
free(context);
#ifdef WIN32
#endif
}
+/**
+ * libwebsocket_context_user() - get the user data associated with the context
+ * @context: Websocket context
+ *
+ * This returns the optional user allocation that can be attached to
+ * the context the sockets live in at context_create time. It's a way
+ * to let all sockets serviced in the same context share data without
+ * using globals statics in the user code.
+ */
LWS_EXTERN void *
libwebsocket_context_user(struct libwebsocket_context *context)
{
- return context->user_space;
+ return context->user_space;
}
/**
*
* 1) Accept new connections to our context's server
*
- * 2) Perform pending broadcast writes initiated from other forked
- * processes (effectively serializing asynchronous broadcasts)
- *
- * 3) Call the receive callback for incoming frame data received by
+ * 2) Call the receive callback for incoming frame data received by
* server or client connections.
*
* You need to call this service function periodically to all the above
* nothing is pending, or as soon as it services whatever was pending.
*/
-
int
libwebsocket_service(struct libwebsocket_context *context, int timeout_ms)
{
return 0;
}
+#ifndef LWS_NO_EXTENSIONS
int
lws_any_extension_handled(struct libwebsocket_context *context,
struct libwebsocket *wsi,
return NULL;
}
+#endif
/**
* libwebsocket_callback_on_writable() - Request a callback when this socket
libwebsocket_callback_on_writable(struct libwebsocket_context *context,
struct libwebsocket *wsi)
{
+#ifndef LWS_NO_EXTENSIONS
int n;
int handled = 0;
if (handled)
return 1;
-
+#endif
if (wsi->position_in_fds_table < 0) {
- lwsl_err("libwebsocket_callback_on_writable: "
- "failed to find socket %d\n", wsi->sock);
+ lwsl_err("libwebsocket_callback_on_writable: failed to find socket %d\n",
+ wsi->sock);
return -1;
}
/* external POLL support via protocol 0 */
context->protocols[0].callback(context, wsi,
LWS_CALLBACK_SET_MODE_POLL_FD,
- (void *)(long)wsi->sock, NULL, POLLOUT);
+ wsi->user_space, (void *)(long)wsi->sock, POLLOUT);
return 1;
}
return wsi->sock;
}
+#ifdef LWS_LATENCY
+void
+lws_latency(struct libwebsocket_context *context, struct libwebsocket *wsi,
+ const char *action, int ret, int completed)
+{
+ struct timeval tv;
+ unsigned long u;
+ char buf[256];
+
+ gettimeofday(&tv, NULL);
+
+ u = (tv.tv_sec * 1000000) + tv.tv_usec;
+
+ if (action) {
+ if (completed) {
+ if (wsi->action_start == wsi->latency_start)
+ sprintf(buf,
+ "Completion first try lat %luus: %p: ret %d: %s\n",
+ u - wsi->latency_start,
+ (void *)wsi, ret, action);
+ else
+ sprintf(buf,
+ "Completion %luus: lat %luus: %p: ret %d: %s\n",
+ u - wsi->action_start,
+ u - wsi->latency_start,
+ (void *)wsi, ret, action);
+ wsi->action_start = 0;
+ } else
+ sprintf(buf, "lat %luus: %p: ret %d: %s\n",
+ u - wsi->latency_start,
+ (void *)wsi, ret, action);
+ if (u - wsi->latency_start > context->worst_latency) {
+ context->worst_latency = u - wsi->latency_start;
+ strcpy(context->worst_latency_info, buf);
+ }
+ lwsl_latency("%s", buf);
+ } else {
+ wsi->latency_start = u;
+ if (!wsi->action_start)
+ wsi->action_start = u;
+ }
+}
+#endif
+
#ifdef LWS_NO_SERVER
int
_libwebsocket_rx_flow_control(struct libwebsocket *wsi)
struct libwebsocket_context *context = wsi->protocol->owning_server;
int n;
- if (!(wsi->rxflow_change_to & 2))
+ if (!(wsi->u.ws.rxflow_change_to & 2))
return 0;
- wsi->rxflow_change_to &= ~2;
+ wsi->u.ws.rxflow_change_to &= ~2;
- lwsl_info("rxflow: wsi %p change_to %d\n", wsi, wsi->rxflow_change_to);
+ lwsl_info("rxflow: wsi %p change_to %d\n",
+ wsi, wsi->u.ws.rxflow_change_to);
/* if we're letting it come again, did we interrupt anything? */
- if ((wsi->rxflow_change_to & 1) && wsi->rxflow_buffer) {
+ if ((wsi->u.ws.rxflow_change_to & 1) && wsi->u.ws.rxflow_buffer) {
n = libwebsocket_interpret_incoming_packet(wsi, NULL, 0);
if (n < 0) {
- libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS);
+ lwsl_info("libwebsocket_rx_flow_control: close req\n");
return -1;
}
if (n)
return 0;
}
- if (wsi->rxflow_change_to & 1)
+ if (wsi->u.ws.rxflow_change_to & 1)
context->fds[wsi->position_in_fds_table].events |= POLLIN;
else
context->fds[wsi->position_in_fds_table].events &= ~POLLIN;
- if (wsi->rxflow_change_to & 1)
+ if (wsi->u.ws.rxflow_change_to & 1)
/* external POLL support via protocol 0 */
context->protocols[0].callback(context, wsi,
LWS_CALLBACK_SET_MODE_POLL_FD,
- (void *)(long)wsi->sock, NULL, POLLIN);
+ wsi->user_space, (void *)(long)wsi->sock, POLLIN);
else
/* external POLL support via protocol 0 */
context->protocols[0].callback(context, wsi,
LWS_CALLBACK_CLEAR_MODE_POLL_FD,
- (void *)(long)wsi->sock, NULL, POLLIN);
+ wsi->user_space, (void *)(long)wsi->sock, POLLIN);
return 1;
}
int
libwebsocket_rx_flow_control(struct libwebsocket *wsi, int enable)
{
- wsi->rxflow_change_to = 2 | !!enable;
+ wsi->u.ws.rxflow_change_to = 2 | !!enable;
return 0;
}
#endif
int user_callback_handle_rxflow(callback_function callback_function,
- struct libwebsocket_context * context,
+ struct libwebsocket_context *context,
struct libwebsocket *wsi,
enum libwebsocket_callback_reasons reason, void *user,
void *in, size_t len)
int n;
n = callback_function(context, wsi, reason, user, in, len);
- if (n < 0)
- return n;
-
- _libwebsocket_rx_flow_control(wsi);
+ if (!n)
+ n = _libwebsocket_rx_flow_control(wsi);
- return 0;
+ return n;
}
/**
* libwebsocket_create_context() - Create the websocket handler
- * @port: Port to listen on... you can use 0 to suppress listening on
- * any port, that's what you want if you are not running a
- * websocket server at all but just using it as a client
- * @interf: NULL to bind the listen socket to all interfaces, or the
- * interface name, eg, "eth2"
- * @protocols: Array of structures listing supported protocols and a protocol-
- * specific callback for each one. The list is ended with an
- * entry that has a NULL callback pointer.
- * It's not const because we write the owning_server member
- * @extensions: NULL or array of libwebsocket_extension structs listing the
- * extensions this context supports
- * @ssl_cert_filepath: If libwebsockets was compiled to use ssl, and you want
- * to listen using SSL, set to the filepath to fetch the
- * server cert from, otherwise NULL for unencrypted
- * @ssl_private_key_filepath: filepath to private key if wanting SSL mode,
- * else ignored
- * @ssl_ca_filepath: CA certificate filepath or NULL
- * @gid: group id to change to after setting listen socket, or -1.
- * @uid: user id to change to after setting listen socket, or -1.
- * @options: 0, or LWS_SERVER_OPTION_DEFEAT_CLIENT_MASK
- * @user: optional user pointer that can be recovered via the context
- * pointer using libwebsocket_context_user
+ * @info: pointer to struct with parameters
*
- * This function creates the listening socket and takes care
+ * This function creates the listening socket (if serving) and takes care
* of all initialization in one step.
*
* After initialization, it returns a struct libwebsocket_context * that
*/
struct libwebsocket_context *
-libwebsocket_create_context(int port, const char *interf,
- struct libwebsocket_protocols *protocols,
- struct libwebsocket_extension *extensions,
- const char *ssl_cert_filepath,
- const char *ssl_private_key_filepath,
- const char *ssl_ca_filepath,
- int gid, int uid, unsigned int options,
- void *user)
+libwebsocket_create_context(struct lws_context_creation_info *info)
{
- int n;
- int m;
- int fd;
- struct sockaddr_in serv_addr, cli_addr;
- int opt = 1;
struct libwebsocket_context *context = NULL;
- unsigned int slen;
char *p;
+ int n;
+#ifndef LWS_NO_SERVER
+ int opt = 1;
struct libwebsocket *wsi;
+ struct sockaddr_in serv_addr;
+#endif
+#ifndef LWS_NO_EXTENSIONS
+ int m;
+ struct libwebsocket_extension *ext;
+#endif
#ifdef LWS_OPENSSL_SUPPORT
SSL_METHOD *method;
- char ssl_err_buf[512];
#endif
- lwsl_info("Initial logging level %d\n", log_level);
- lwsl_info(" LWS_MAX_HEADER_NAME_LENGTH: %u\n", LWS_MAX_HEADER_NAME_LENGTH);
+#ifndef LWS_NO_DAEMONIZE
+ int pid_daemon = get_daemonize_pid();
+#endif
+
+ lwsl_notice("Initial logging level %d\n", log_level);
+ lwsl_notice("Library version: %s\n", library_version);
lwsl_info(" LWS_MAX_HEADER_LEN: %u\n", LWS_MAX_HEADER_LEN);
- lwsl_info(" LWS_INITIAL_HDR_ALLOC: %u\n", LWS_INITIAL_HDR_ALLOC);
- lwsl_info(" LWS_ADDITIONAL_HDR_ALLOC: %u\n", LWS_ADDITIONAL_HDR_ALLOC);
- lwsl_info(" MAX_USER_RX_BUFFER: %u\n", MAX_USER_RX_BUFFER);
- lwsl_info(" MAX_BROADCAST_PAYLOAD: %u\n", MAX_BROADCAST_PAYLOAD);
lwsl_info(" LWS_MAX_PROTOCOLS: %u\n", LWS_MAX_PROTOCOLS);
- lwsl_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n", LWS_MAX_EXTENSIONS_ACTIVE);
+#ifndef LWS_NO_EXTENSIONS
+ lwsl_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n",
+ LWS_MAX_EXTENSIONS_ACTIVE);
+#else
+ lwsl_notice(" Configured without extension support\n");
+#endif
lwsl_info(" SPEC_LATEST_SUPPORTED: %u\n", SPEC_LATEST_SUPPORTED);
lwsl_info(" AWAITING_TIMEOUT: %u\n", AWAITING_TIMEOUT);
- lwsl_info(" CIPHERS_LIST_STRING: '%s'\n", CIPHERS_LIST_STRING);
+ if (info->ssl_cipher_list)
+ lwsl_info(" SSL ciphers: '%s'\n", info->ssl_cipher_list);
lwsl_info(" SYSTEM_RANDOM_FILEPATH: '%s'\n", SYSTEM_RANDOM_FILEPATH);
lwsl_info(" LWS_MAX_ZLIB_CONN_BUFFER: %u\n", LWS_MAX_ZLIB_CONN_BUFFER);
wsdll = GetModuleHandle(_T("Ws2_32.dll"));
if (wsdll)
poll = (PFNWSAPOLL)GetProcAddress(wsdll, "WSAPoll");
+
+ /* Finally fall back to emulated poll if all else fails */
+ if (!poll)
+ poll = emulated_poll;
}
#endif
-
- context = (struct libwebsocket_context *) malloc(sizeof(struct libwebsocket_context));
+ context = (struct libwebsocket_context *)
+ malloc(sizeof(struct libwebsocket_context));
if (!context) {
lwsl_err("No memory for websocket context\n");
return NULL;
}
- context->protocols = protocols;
- context->listen_port = port;
+ memset(context, 0, sizeof(*context));
+#ifndef LWS_NO_DAEMONIZE
+ context->started_with_parent = pid_daemon;
+ lwsl_notice(" Started with daemon pid %d\n", pid_daemon);
+#endif
+
+ context->listen_service_extraseen = 0;
+ context->protocols = info->protocols;
+ context->listen_port = info->port;
context->http_proxy_port = 0;
context->http_proxy_address[0] = '\0';
- context->options = options;
+ context->options = info->options;
/* to reduce this allocation, */
context->max_fds = getdtablesize();
- lwsl_info(" max fd tracked: %u\n", context->max_fds);
-
- context->fds = (struct pollfd *)malloc(sizeof(struct pollfd) * context->max_fds);
+ lwsl_notice(" static allocation: %u + (%u x %u fds) = %u bytes\n",
+ sizeof(struct libwebsocket_context),
+ sizeof(struct pollfd) + sizeof(struct libwebsocket *),
+ context->max_fds,
+ sizeof(struct libwebsocket_context) +
+ ((sizeof(struct pollfd) + sizeof(struct libwebsocket *)) *
+ context->max_fds));
+
+ context->fds = (struct pollfd *)malloc(sizeof(struct pollfd) *
+ context->max_fds);
if (context->fds == NULL) {
- lwsl_err("Unable to allocate fds array for %d connections\n", context->max_fds);
+ lwsl_err("Unable to allocate fds array for %d connections\n",
+ context->max_fds);
free(context);
return NULL;
}
- context->lws_lookup = (struct libwebsocket **)malloc(sizeof(struct libwebsocke *) * context->max_fds);
+ context->lws_lookup = (struct libwebsocket **)
+ malloc(sizeof(struct libwebsocket *) * context->max_fds);
if (context->lws_lookup == NULL) {
- lwsl_err("Unable to allocate lws_lookup array for %d connections\n", context->max_fds);
+ lwsl_err(
+ "Unable to allocate lws_lookup array for %d connections\n",
+ context->max_fds);
free(context->fds);
free(context);
return NULL;
}
+
context->fds_count = 0;
- context->extensions = extensions;
+#ifndef LWS_NO_EXTENSIONS
+ context->extensions = info->extensions;
+#endif
context->last_timeout_check_s = 0;
- context->user_space = user;
+ context->user_space = info->user;
#ifdef WIN32
context->fd_random = 0;
#else
context->fd_random = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY);
if (context->fd_random < 0) {
- free(context);
lwsl_err("Unable to open random device %s %d\n",
SYSTEM_RANDOM_FILEPATH, context->fd_random);
- return NULL;
+ goto bail;
}
#endif
strcpy(context->canonical_hostname, "unknown");
#ifndef LWS_NO_SERVER
- if (!(options & LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME)) {
- struct sockaddr sa;
- char hostname[1024] = "";
-
+ if (!(info->options & LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME)) {
/* find canonical hostname */
+ gethostname((char *)context->canonical_hostname,
+ sizeof(context->canonical_hostname) - 1);
- hostname[(sizeof hostname) - 1] = '\0';
- memset(&sa, 0, sizeof(sa));
- sa.sa_family = AF_INET;
- sa.sa_data[(sizeof sa.sa_data) - 1] = '\0';
- gethostname(hostname, (sizeof hostname) - 1);
-
- n = 0;
-
- if (strlen(hostname) < sizeof(sa.sa_data) - 1) {
- strcpy(sa.sa_data, hostname);
- // lwsl_debug("my host name is %s\n", sa.sa_data);
- n = getnameinfo(&sa, sizeof(sa), hostname,
- (sizeof hostname) - 1, NULL, 0, 0);
- }
-
- if (!n) {
- strncpy(context->canonical_hostname, hostname,
- sizeof context->canonical_hostname - 1);
- context->canonical_hostname[
- sizeof context->canonical_hostname - 1] = '\0';
- } else
- strncpy(context->canonical_hostname, hostname,
- sizeof context->canonical_hostname - 1);
-
- lwsl_info(" canonical_hostname = %s\n", context->canonical_hostname);
+ lwsl_notice(" canonical_hostname = %s\n",
+ context->canonical_hostname);
}
#endif
p = getenv("http_proxy");
if (p) {
strncpy(context->http_proxy_address, p,
- sizeof context->http_proxy_address - 1);
+ sizeof(context->http_proxy_address) - 1);
context->http_proxy_address[
- sizeof context->http_proxy_address - 1] = '\0';
+ sizeof(context->http_proxy_address) - 1] = '\0';
p = strchr(context->http_proxy_address, ':');
if (p == NULL) {
lwsl_err("http_proxy needs to be ads:port\n");
- return NULL;
+ goto bail;
}
*p = '\0';
context->http_proxy_port = atoi(p + 1);
- lwsl_info(" Proxy %s:%u\n",
+ lwsl_notice(" Proxy %s:%u\n",
context->http_proxy_address,
context->http_proxy_port);
}
#ifndef LWS_NO_SERVER
- if (port) {
+ if (info->port) {
#ifdef LWS_OPENSSL_SUPPORT
- context->use_ssl = ssl_cert_filepath != NULL &&
- ssl_private_key_filepath != NULL;
+ context->use_ssl = info->ssl_cert_filepath != NULL &&
+ info->ssl_private_key_filepath != NULL;
+#ifdef USE_CYASSL
+ lwsl_notice(" Compiled with CYASSL support\n");
+#else
+ lwsl_notice(" Compiled with OpenSSL support\n");
+#endif
if (context->use_ssl)
- lwsl_info(" Compiled with SSL support, using it\n");
+ lwsl_notice(" Using SSL mode\n");
else
- lwsl_info(" Compiled with SSL support, not using it\n");
+ lwsl_notice(" Using non-SSL mode\n");
#else
- if (ssl_cert_filepath != NULL &&
- ssl_private_key_filepath != NULL) {
- lwsl_info(" Not compiled for OpenSSl support!\n");
- return NULL;
+ if (info->ssl_cert_filepath != NULL &&
+ info->ssl_private_key_filepath != NULL) {
+ lwsl_notice(" Not compiled for OpenSSl support!\n");
+ goto bail;
}
- lwsl_info(" Compiled without SSL support, "
- "serving unencrypted\n");
+ lwsl_notice(" Compiled without SSL support\n");
#endif
+
+ lwsl_notice(
+ " per-conn mem: %u + %u headers + protocol rx buf\n",
+ sizeof(struct libwebsocket),
+ sizeof(struct allocated_headers));
}
#endif
method = (SSL_METHOD *)SSLv23_server_method();
if (!method) {
- lwsl_err("problem creating ssl method: %s\n",
- ERR_error_string(ERR_get_error(), ssl_err_buf));
- return NULL;
+ lwsl_err("problem creating ssl method %lu: %s\n",
+ ERR_get_error(),
+ ERR_error_string(ERR_get_error(),
+ (char *)context->service_buffer));
+ goto bail;
}
context->ssl_ctx = SSL_CTX_new(method); /* create context */
if (!context->ssl_ctx) {
- lwsl_err("problem creating ssl context: %s\n",
- ERR_error_string(ERR_get_error(), ssl_err_buf));
- return NULL;
+ lwsl_err("problem creating ssl context %lu: %s\n",
+ ERR_get_error(),
+ ERR_error_string(ERR_get_error(),
+ (char *)context->service_buffer));
+ goto bail;
}
#ifdef SSL_OP_NO_COMPRESSION
SSL_CTX_set_options(context->ssl_ctx, SSL_OP_NO_COMPRESSION);
#endif
SSL_CTX_set_options(context->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
- SSL_CTX_set_cipher_list(context->ssl_ctx, CIPHERS_LIST_STRING);
+ if (info->ssl_cipher_list)
+ SSL_CTX_set_cipher_list(context->ssl_ctx,
+ info->ssl_cipher_list);
#ifndef LWS_NO_CLIENT
/* client context */
- if (port == CONTEXT_PORT_NO_LISTEN) {
+ if (info->port == CONTEXT_PORT_NO_LISTEN) {
method = (SSL_METHOD *)SSLv23_client_method();
if (!method) {
- lwsl_err("problem creating ssl method: %s\n",
- ERR_error_string(ERR_get_error(), ssl_err_buf));
- return NULL;
+ lwsl_err("problem creating ssl method %lu: %s\n",
+ ERR_get_error(),
+ ERR_error_string(ERR_get_error(),
+ (char *)context->service_buffer));
+ goto bail;
}
/* create context */
context->ssl_client_ctx = SSL_CTX_new(method);
if (!context->ssl_client_ctx) {
- lwsl_err("problem creating ssl context: %s\n",
- ERR_error_string(ERR_get_error(), ssl_err_buf));
- return NULL;
+ lwsl_err("problem creating ssl context %lu: %s\n",
+ ERR_get_error(),
+ ERR_error_string(ERR_get_error(),
+ (char *)context->service_buffer));
+ goto bail;
}
#ifdef SSL_OP_NO_COMPRESSION
- SSL_CTX_set_options(context->ssl_client_ctx, SSL_OP_NO_COMPRESSION);
+ SSL_CTX_set_options(context->ssl_client_ctx,
+ SSL_OP_NO_COMPRESSION);
#endif
- SSL_CTX_set_options(context->ssl_client_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
- SSL_CTX_set_cipher_list(context->ssl_client_ctx, CIPHERS_LIST_STRING);
+ SSL_CTX_set_options(context->ssl_client_ctx,
+ SSL_OP_CIPHER_SERVER_PREFERENCE);
+ if (info->ssl_cipher_list)
+ SSL_CTX_set_cipher_list(context->ssl_client_ctx,
+ info->ssl_cipher_list);
/* openssl init for cert verification (for client sockets) */
- if (!ssl_ca_filepath) {
+ if (!info->ssl_ca_filepath) {
if (!SSL_CTX_load_verify_locations(
context->ssl_client_ctx, NULL,
LWS_OPENSSL_CLIENT_CERTS))
lwsl_err(
- "Unable to load SSL Client certs from %s "
- "(set by --with-client-cert-dir= in configure) -- "
- " client ssl isn't going to work",
- LWS_OPENSSL_CLIENT_CERTS);
+ "Unable to load SSL Client certs from %s "
+ "(set by --with-client-cert-dir= "
+ "in configure) -- client ssl isn't "
+ "going to work", LWS_OPENSSL_CLIENT_CERTS);
} else
if (!SSL_CTX_load_verify_locations(
- context->ssl_client_ctx, ssl_ca_filepath,
+ context->ssl_client_ctx, info->ssl_ca_filepath,
NULL))
lwsl_err(
"Unable to load SSL Client certs "
"file from %s -- client ssl isn't "
- "going to work", ssl_ca_filepath);
+ "going to work", info->ssl_ca_filepath);
/*
* callback allowing user code to load extra verification certs
/* as a server, are we requiring clients to identify themselves? */
- if (options & LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT) {
+ if (info->options &
+ LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT) {
/* absolutely require the client cert */
/* set the local certificate from CertFile */
n = SSL_CTX_use_certificate_chain_file(context->ssl_ctx,
- ssl_cert_filepath);
+ info->ssl_cert_filepath);
if (n != 1) {
- lwsl_err("problem getting cert '%s': %s\n",
- ssl_cert_filepath,
- ERR_error_string(ERR_get_error(), ssl_err_buf));
- return NULL;
+ lwsl_err("problem getting cert '%s' %lu: %s\n",
+ info->ssl_cert_filepath,
+ ERR_get_error(),
+ ERR_error_string(ERR_get_error(),
+ (char *)context->service_buffer));
+ goto bail;
}
/* set the private key from KeyFile */
if (SSL_CTX_use_PrivateKey_file(context->ssl_ctx,
- ssl_private_key_filepath, SSL_FILETYPE_PEM) != 1) {
- lwsl_err("ssl problem getting key '%s': %s\n",
- ssl_private_key_filepath,
- ERR_error_string(ERR_get_error(), ssl_err_buf));
- return NULL;
+ info->ssl_private_key_filepath,
+ SSL_FILETYPE_PEM) != 1) {
+ lwsl_err("ssl problem getting key '%s' %lu: %s\n",
+ info->ssl_private_key_filepath,
+ ERR_get_error(),
+ ERR_error_string(ERR_get_error(),
+ (char *)context->service_buffer));
+ goto bail;
}
/* verify private key */
if (!SSL_CTX_check_private_key(context->ssl_ctx)) {
lwsl_err("Private SSL key doesn't match cert\n");
- return NULL;
+ goto bail;
}
/* SSL is happy and has a cert it's content with */
/* selftest */
if (lws_b64_selftest())
- return NULL;
+ goto bail;
#ifndef LWS_NO_SERVER
/* set up our external listening socket we serve on */
- if (port) {
- extern int interface_to_sa(const char *ifname, struct sockaddr_in *addr, size_t addrlen);
+ if (info->port) {
int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
lwsl_err("ERROR opening socket\n");
- return NULL;
+ goto bail;
}
- /* allow us to restart even if old sockets in TIME_WAIT */
+#ifndef WIN32
+ /*
+ * allow us to restart even if old sockets in TIME_WAIT
+ * (REUSEADDR on Unix means, "don't hang on to this
+ * address after the listener is closed." On Windows, though,
+ * it means "don't keep other processes from binding to
+ * this address while we're using it)
+ */
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
(const void *)&opt, sizeof(opt));
+#endif
/* Disable Nagle */
opt = 1;
setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY,
(const void *)&opt, sizeof(opt));
+ #ifdef WIN32
+ opt = 0;
+ ioctlsocket(sockfd, FIONBIO, (unsigned long *)&opt);
+ #else
+ fcntl(sockfd, F_SETFL, O_NONBLOCK);
+ #endif
+
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
- if (interf == NULL)
+ if (info->iface == NULL)
serv_addr.sin_addr.s_addr = INADDR_ANY;
else
- interface_to_sa(interf, &serv_addr,
+ interface_to_sa(info->iface, &serv_addr,
sizeof(serv_addr));
- serv_addr.sin_port = htons(port);
+ serv_addr.sin_port = htons(info->port);
n = bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr));
if (n < 0) {
lwsl_err("ERROR on binding to port %d (%d %d)\n",
- port, n, errno);
+ info->port, n, errno);
close(sockfd);
- return NULL;
+ goto bail;
}
- wsi = (struct libwebsocket *)malloc(sizeof(struct libwebsocket));
+ wsi = (struct libwebsocket *)malloc(
+ sizeof(struct libwebsocket));
if (wsi == NULL) {
lwsl_err("Out of mem\n");
close(sockfd);
- return NULL;
+ goto bail;
}
- memset(wsi, 0, sizeof (struct libwebsocket));
+ memset(wsi, 0, sizeof(struct libwebsocket));
wsi->sock = sockfd;
+#ifndef LWS_NO_EXTENSIONS
wsi->count_active_extensions = 0;
+#endif
wsi->mode = LWS_CONNMODE_SERVER_LISTENER;
insert_wsi_socket_into_fds(context, wsi);
context->listen_service_fd = sockfd;
listen(sockfd, LWS_SOMAXCONN);
- lwsl_info(" Listening on port %d\n", port);
+ lwsl_notice(" Listening on port %d\n", info->port);
}
#endif
*/
#ifdef WIN32
#else
- if (gid != -1)
- if (setgid(gid))
+ if (info->gid != -1)
+ if (setgid(info->gid))
lwsl_warn("setgid: %s\n", strerror(errno));
- if (uid != -1)
- if (setuid(uid))
+ if (info->uid != -1)
+ if (setuid(info->uid))
lwsl_warn("setuid: %s\n", strerror(errno));
#endif
- /* set up our internal broadcast trigger sockets per-protocol */
+ /* initialize supported protocols */
for (context->count_protocols = 0;
- protocols[context->count_protocols].callback;
+ info->protocols[context->count_protocols].callback;
context->count_protocols++) {
lwsl_parser(" Protocol: %s\n",
- protocols[context->count_protocols].name);
-
- protocols[context->count_protocols].owning_server = context;
- protocols[context->count_protocols].protocol_index =
- context->count_protocols;
-
- fd = socket(AF_INET, SOCK_STREAM, 0);
- if (fd < 0) {
- lwsl_err("ERROR opening socket\n");
- return NULL;
- }
-
- /* allow us to restart even if old sockets in TIME_WAIT */
- setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt,
- sizeof(opt));
-
- bzero((char *) &serv_addr, sizeof(serv_addr));
- serv_addr.sin_family = AF_INET;
- serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
- serv_addr.sin_port = 0; /* pick the port for us */
-
- n = bind(fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
- if (n < 0) {
- lwsl_err("ERROR on binding to port %d (%d %d)\n",
- port, n, errno);
- return NULL;
- }
-
- slen = sizeof cli_addr;
- n = getsockname(fd, (struct sockaddr *)&cli_addr, &slen);
- if (n < 0) {
- lwsl_err("getsockname failed\n");
- return NULL;
- }
- protocols[context->count_protocols].broadcast_socket_port =
- ntohs(cli_addr.sin_port);
- listen(fd, 5);
-
- lwsl_debug(" Protocol %s broadcast socket %d\n",
- protocols[context->count_protocols].name,
- ntohs(cli_addr.sin_port));
+ info->protocols[context->count_protocols].name);
- /* dummy wsi per broadcast proxy socket */
-
- wsi = (struct libwebsocket *)malloc(sizeof(struct libwebsocket));
- if (wsi == NULL) {
- lwsl_err("Out of mem\n");
- close(fd);
- return NULL;
- }
- memset(wsi, 0, sizeof (struct libwebsocket));
- wsi->sock = fd;
- wsi->mode = LWS_CONNMODE_BROADCAST_PROXY_LISTENER;
- wsi->count_active_extensions = 0;
- /* note which protocol we are proxying */
- wsi->protocol_index_for_broadcast_proxy =
+ info->protocols[context->count_protocols].owning_server =
+ context;
+ info->protocols[context->count_protocols].protocol_index =
context->count_protocols;
- insert_wsi_socket_into_fds(context, wsi);
+ /*
+ * inform all the protocols that they are doing their one-time
+ * initialization if they want to
+ */
+ info->protocols[context->count_protocols].callback(context,
+ NULL, LWS_CALLBACK_PROTOCOL_INIT, NULL, NULL, 0);
}
+#ifndef LWS_NO_EXTENSIONS
/*
* give all extensions a chance to create any per-context
* allocations they need
*/
m = LWS_EXT_CALLBACK_CLIENT_CONTEXT_CONSTRUCT;
- if (port)
+ if (info->port)
m = LWS_EXT_CALLBACK_SERVER_CONTEXT_CONSTRUCT;
-
- if (extensions) {
- while (extensions->callback) {
- lwsl_ext(" Extension: %s\n", extensions->name);
- extensions->callback(context, extensions, NULL,
+
+ if (info->extensions) {
+ ext = info->extensions;
+ while (ext->callback) {
+ lwsl_ext(" Extension: %s\n", ext->name);
+ ext->callback(context, ext, NULL,
(enum libwebsocket_extension_callback_reasons)m,
NULL, NULL, 0);
- extensions++;
- }
- }
-
- return context;
-}
-
-
-#ifndef LWS_NO_FORK
-
-/**
- * libwebsockets_fork_service_loop() - Optional helper function forks off
- * a process for the websocket server loop.
- * You don't have to use this but if not, you
- * have to make sure you are calling
- * libwebsocket_service periodically to service
- * the websocket traffic
- * @context: server context returned by creation function
- */
-
-int
-libwebsockets_fork_service_loop(struct libwebsocket_context *context)
-{
- int fd;
- struct sockaddr_in cli_addr;
- int n;
- int p;
-
- n = fork();
- if (n < 0)
- return n;
-
- if (!n) {
-
- /* main process context */
-
- /*
- * set up the proxy sockets to allow broadcast from
- * service process context
- */
-
- for (p = 0; p < context->count_protocols; p++) {
- fd = socket(AF_INET, SOCK_STREAM, 0);
- if (fd < 0) {
- lwsl_err("Unable to create socket\n");
- return -1;
- }
- cli_addr.sin_family = AF_INET;
- cli_addr.sin_port = htons(
- context->protocols[p].broadcast_socket_port);
- cli_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
- n = connect(fd, (struct sockaddr *)&cli_addr,
- sizeof cli_addr);
- if (n < 0) {
- lwsl_err("Unable to connect to "
- "broadcast socket %d, %s\n",
- n, strerror(errno));
- return -1;
- }
-
- context->protocols[p].broadcast_socket_user_fd = fd;
+ ext++;
}
-
- return 0;
}
-
-#ifdef HAVE_SYS_PRCTL_H
- /* we want a SIGHUP when our parent goes down */
- prctl(PR_SET_PDEATHSIG, SIGHUP);
#endif
+ return context;
- /* in this forked process, sit and service websocket connections */
-
- while (1) {
- if (libwebsocket_service(context, 1000))
- break;
-//#ifndef HAVE_SYS_PRCTL_H
-/*
- * on systems without prctl() (i.e. anything but linux) we can notice that our
- * parent is dead if getppid() returns 1. FIXME apparently this is not true for
- * solaris, could remember ppid right after fork and wait for it to change.
- */
-
- if (getppid() == 1)
- break;
-//#endif
- }
-
-
- return 1;
+bail:
+ libwebsocket_context_destroy(context);
+ return NULL;
}
-#endif
-
/**
* libwebsockets_get_protocol() - Returns a protocol pointer from a websocket
* connection.
* @wsi: pointer to struct websocket you want to know the protocol of
*
*
- * This is useful to get the protocol to broadcast back to from inside
- * the callback.
+ * Some apis can act on all live connections of a given protocol,
+ * this is how you can get a pointer to the active protocol if needed.
*/
const struct libwebsocket_protocols *
return wsi->protocol;
}
-/**
- * libwebsockets_broadcast() - Sends a buffer to the callback for all active
- * connections of the given protocol.
- * @protocol: pointer to the protocol you will broadcast to all members of
- * @buf: buffer containing the data to be broadcase. NOTE: this has to be
- * allocated with LWS_SEND_BUFFER_PRE_PADDING valid bytes before
- * the pointer and LWS_SEND_BUFFER_POST_PADDING afterwards in the
- * case you are calling this function from callback context.
- * @len: length of payload data in buf, starting from buf.
- *
- * This function allows bulk sending of a packet to every connection using
- * the given protocol. It does not send the data directly; instead it calls
- * the callback with a reason type of LWS_CALLBACK_BROADCAST. If the callback
- * wants to actually send the data for that connection, the callback itself
- * should call libwebsocket_write().
- *
- * libwebsockets_broadcast() can be called from another fork context without
- * having to take any care about data visibility between the processes, it'll
- * "just work".
- */
-
-
-int
-libwebsockets_broadcast(const struct libwebsocket_protocols *protocol,
- unsigned char *buf, size_t len)
-{
- struct libwebsocket_context *context = protocol->owning_server;
- int n;
- struct libwebsocket *wsi;
-
- if (!protocol->broadcast_socket_user_fd) {
- /*
- * We are either running unforked / flat, or we are being
- * called from poll thread context
- * eg, from a callback. In that case don't use sockets for
- * broadcast IPC (since we can't open a socket connection to
- * a socket listening on our own thread) but directly do the
- * send action.
- *
- * Locking is not needed because we are by definition being
- * called in the poll thread context and are serialized.
- */
-
- for (n = 0; n < context->fds_count; n++) {
-
- wsi = context->lws_lookup[context->fds[n].fd];
- if (!wsi)
- continue;
-
- if (wsi->mode != LWS_CONNMODE_WS_SERVING)
- continue;
-
- /*
- * never broadcast to non-established connections
- */
- if (wsi->state != WSI_STATE_ESTABLISHED)
- continue;
-
- /* only broadcast to guys using
- * requested protocol
- */
- if (wsi->protocol != protocol)
- continue;
-
- user_callback_handle_rxflow(wsi->protocol->callback,
- context, wsi,
- LWS_CALLBACK_BROADCAST,
- wsi->user_space,
- buf, len);
- }
-
- return 0;
- }
-
- /*
- * We're being called from a different process context than the server
- * loop. Instead of broadcasting directly, we send our
- * payload on a socket to do the IPC; the server process will serialize
- * the broadcast action in its main poll() loop.
- *
- * There's one broadcast socket listening for each protocol supported
- * set up when the websocket server initializes
- */
-
- n = send(protocol->broadcast_socket_user_fd, buf, len, MSG_NOSIGNAL);
-
- return n;
-}
-
int
libwebsocket_is_final_fragment(struct libwebsocket *wsi)
{
- return wsi->final;
+ return wsi->u.ws.final;
}
unsigned char
libwebsocket_get_reserved_bits(struct libwebsocket *wsi)
{
- return wsi->rsv;
+ return wsi->u.ws.rsv;
}
-void *
+int
libwebsocket_ensure_user_space(struct libwebsocket *wsi)
{
+ if (!wsi->protocol)
+ return 1;
+
/* allocate the per-connection user memory (if any) */
if (wsi->protocol->per_session_data_size && !wsi->user_space) {
wsi->protocol->per_session_data_size);
if (wsi->user_space == NULL) {
lwsl_err("Out of memory for conn user space\n");
- return NULL;
+ return 1;
}
memset(wsi->user_space, 0,
wsi->protocol->per_session_data_size);
}
- return wsi->user_space;
-}
-
-/**
- * lws_confirm_legit_wsi: returns nonzero if the wsi looks bad
- *
- * @wsi: struct libwebsocket to assess
- *
- * Performs consistecy checks on what the wsi claims and what the
- * polling arrays hold. This'll catch a closed wsi still in use.
- * Don't try to use on the listen (nonconnection) wsi as it will
- * fail it. Otherwise 0 return == wsi seems consistent.
- */
-
-int lws_confirm_legit_wsi(struct libwebsocket *wsi)
-{
- struct libwebsocket_context *context;
-
- if (!(wsi && wsi->protocol && wsi->protocol->owning_server))
- return 1;
-
- context = wsi->protocol->owning_server;
-
- if (!context)
- return 2;
-
- if (!wsi->position_in_fds_table)
- return 3; /* position in fds table looks bad */
- if (context->fds[wsi->position_in_fds_table].fd != wsi->sock)
- return 4; /* pollfd entry does not wait on our socket descriptor */
- if (context->lws_lookup[wsi->sock] != wsi)
- return 5; /* lookup table does not agree with wsi */
-
return 0;
}
{
char buf[300];
struct timeval tv;
- int pos = 0;
int n;
gettimeofday(&tv, NULL);
buf[0] = '\0';
for (n = 0; n < LLL_COUNT; n++)
if (level == (1 << n)) {
- pos = sprintf(buf, "[%ld:%04d] %s: ", tv.tv_sec,
- (int)(tv.tv_usec / 100), log_level_names[n]);
+ sprintf(buf, "[%ld:%04d] %s: ", tv.tv_sec,
+ (int)(tv.tv_usec / 100), log_level_names[n]);
break;
}
-
+
fprintf(stderr, "%s%s", buf, line);
}
+#ifdef WIN32
+void lwsl_emit_syslog(int level, const char *line)
+{
+ lwsl_emit_stderr(level, line);
+}
+#else
+void lwsl_emit_syslog(int level, const char *line)
+{
+ int syslog_level = LOG_DEBUG;
+
+ switch (level) {
+ case LLL_ERR:
+ syslog_level = LOG_ERR;
+ break;
+ case LLL_WARN:
+ syslog_level = LOG_WARNING;
+ break;
+ case LLL_NOTICE:
+ syslog_level = LOG_NOTICE;
+ break;
+ case LLL_INFO:
+ syslog_level = LOG_INFO;
+ break;
+ }
+ syslog(syslog_level, "%s", line);
+}
+#endif
+
void _lws_log(int filter, const char *format, ...)
{
char buf[256];
return;
va_start(ap, format);
- vsnprintf(buf, (sizeof buf), format, ap);
- buf[(sizeof buf) - 1] = '\0';
+ vsnprintf(buf, sizeof(buf), format, ap);
+ buf[sizeof(buf) - 1] = '\0';
va_end(ap);
lwsl_emit(filter, buf);
* function to perform log string emission instead of
* the default stderr one.
*
- * log level defaults to "err" and "warn" contexts enabled only and
+ * log level defaults to "err", "warn" and "notice" contexts enabled and
* emission on stderr.
*/
-void lws_set_log_level(int level, void (*log_emit_function)(const char *line))
+void lws_set_log_level(int level, void (*log_emit_function)(int level,
+ const char *line))
{
log_level = level;
if (log_emit_function)