add-per-session-user-data.patch
Signed-off-by: Andy Green <andy@warmcat.com>
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index b11f4f8..065cb01 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -12,6 +12,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
@@ -30,8 +31,10 @@
#endif
void md5(const unsigned char *input, int ilen, unsigned char output[16]);
-static void libwebsocket_service(struct libwebsocket *wsi, int sock);
+static int
+libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len);
+#define MAX_CLIENTS 100
#define LWS_MAX_HEADER_NAME_LENGTH 64
#define LWS_MAX_HEADER_LEN 4096
#define LWS_INITIAL_HDR_ALLOC 256
@@ -89,7 +92,7 @@
struct libwebsocket {
int (*callback)(struct libwebsocket *,
- enum libwebsocket_callback_reasons reason, void *, size_t);
+ enum libwebsocket_callback_reasons reason, void *, void *, size_t);
enum lws_connection_states state;
@@ -104,6 +107,9 @@
enum lws_rx_parse_state lws_rx_parse_state;
size_t rx_packet_length;
+
+ /* last */
+ char user_space[0];
};
@@ -119,11 +125,37 @@
{ "\x0d\x0a", 2 },
};
+static void
+libwebsocket_close_and_free_session(struct libwebsocket *wsi)
+{
+ int n = wsi->state;
+
+ wsi->state = WSI_STATE_DEAD_SOCKET;
+
+ if (wsi->callback && n == WSI_STATE_ESTABLISHED)
+ wsi->callback(wsi, LWS_CALLBACK_CLOSED, &wsi->user_space[0],
+ NULL, 0);
+
+ for (n = 0; n < WSI_TOKEN_COUNT; n++)
+ if (wsi->utf8_token[n].token)
+ free(wsi->utf8_token[n].token);
+
+// fprintf(stderr, "closing fd=%d\n", wsi->sock);
+
+ shutdown(wsi->sock, SHUT_RDWR);
+ close(wsi->sock);
+ free(wsi);
+}
+
/**
* libwebsocket_create_server() - Create the listening websockets server
* @port: Port to listen on
* @callback: The callback in user code to perform actual serving
* @protocol: Which version of the websockets protocol (currently 76)
+ * @user_area_size: How much memory to allocate per connection session
+ * which will be used by the user application to store
+ * per-session data. A pointer to this space is given
+ * when the user callback is called.
*
* This function forks to create the listening socket and takes care
* of all initialization in one step.
@@ -143,50 +175,53 @@
int libwebsocket_create_server(int port,
int (*callback)(struct libwebsocket *,
- enum libwebsocket_callback_reasons, void *, size_t),
- int protocol)
+ enum libwebsocket_callback_reasons,
+ void *, void *, size_t),
+ int protocol, size_t user_area_size)
{
int n;
+ int client;
int sockfd;
- int sessfd;
+ int fd;
unsigned int clilen;
struct sockaddr_in serv_addr, cli_addr;
- int pid;
- struct libwebsocket *wsi = malloc(sizeof(struct libwebsocket));
-
- if (!wsi)
- return -1;
-
- wsi->state = WSI_STATE_HTTP;
- wsi->name_buffer_pos = 0;
+ struct libwebsocket *wsi[MAX_CLIENTS + 1];
+ struct pollfd fds[MAX_CLIENTS + 1];
+ int fds_count = 0;
+ unsigned char buf[256];
+ int opt = 1;
- for (n = 0; n < WSI_TOKEN_COUNT; n++) {
- wsi->utf8_token[n].token = NULL;
- wsi->utf8_token[n].token_len = 0;
- }
-
- wsi->callback = callback;
+ /* sanity check */
+
switch (protocol) {
case 0:
case 2:
case 76:
fprintf(stderr, " Using protocol v%d\n", protocol);
- wsi->ietf_spec_revision = protocol;
break;
default:
fprintf(stderr, "protocol %d not supported (try 0 2 or 76)\n",
protocol);
return -1;
}
+
+ if (!callback) {
+ fprintf(stderr, "callback is not optional!\n");
+ return -1;
+ }
/* sit there listening for connects, accept and spawn session servers */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
fprintf(stderr, "ERROR opening socket");
+ return -1;
}
- bzero((char *) &serv_addr, sizeof(serv_addr));
+
+ /* allow us to restart even if old sockets in TIME_WAIT */
+ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+ bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(port);
@@ -202,66 +237,148 @@
n = fork();
if (n < 0) {
fprintf(stderr, "Failed on forking server thread: %d\n", n);
- exit(1);
+ return -1;
}
/* we are done as far as the caller is concerned */
if (n)
- return 0;
+ return sockfd;
- fprintf(stderr, " Listening on port %d\n", port);
+ /* we are running in a forked subprocess now */
listen(sockfd, 5);
+ fprintf(stderr, " Listening on port %d\n", port);
+
+ fds[0].fd = sockfd;
+ fds_count = 1;
+ fds[0].events = POLLIN;
while (1) {
- clilen = sizeof(cli_addr);
- sessfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
- if (sessfd < 0) {
- fprintf(stderr, "ERROR on accept");
- continue;
+ n = poll(fds, fds_count, 50);
+ if (n < 0 || fds[0].revents & (POLLERR | POLLHUP)) {
+// fprintf(stderr, "Listen Socket dead\n");
+ goto fatal;
}
+ if (n == 0) /* poll timeout */
+ goto poll_out;
+
+ if (fds[0].revents & POLLIN) {
+
+ /* listen socket got a new connection... */
+
+ clilen = sizeof(cli_addr);
+ fd = accept(sockfd, (struct sockaddr *)&cli_addr,
+ &clilen);
+ if (fd < 0) {
+ fprintf(stderr, "ERROR on accept");
+ continue;
+ }
- /* fork off a new server instance */
+ if (fds_count >= MAX_CLIENTS) {
+ fprintf(stderr, "too busy");
+ close(fd);
+ continue;
+ }
- pid = fork();
- if (pid < 0) {
- fprintf(stderr, "ERROR on fork");
- continue;
+// fprintf(stderr, "accepted new conn port %u on fd=%d\n",
+// ntohs(cli_addr.sin_port), fd);
+
+ /* intialize the instance struct */
+
+ wsi[fds_count] = malloc(sizeof(struct libwebsocket) +
+ user_area_size);
+ if (!wsi[fds_count])
+ return -1;
+
+ wsi[fds_count]->sock = fd;
+ wsi[fds_count]->state = WSI_STATE_HTTP;
+ wsi[fds_count]->name_buffer_pos = 0;
+
+ for (n = 0; n < WSI_TOKEN_COUNT; n++) {
+ wsi[fds_count]->utf8_token[n].token = NULL;
+ wsi[fds_count]->utf8_token[n].token_len = 0;
+ }
+
+ wsi[fds_count]->callback = callback;
+ wsi[fds_count]->ietf_spec_revision = protocol;
+
+ fds[fds_count].events = POLLIN;
+ fds[fds_count++].fd = fd;
}
- if (pid) {
- close(sessfd);
- continue;
+ /* check for activity on client sockets */
+
+ for (client = 1; client < fds_count; client++) {
+
+ /* handle session socket closed */
+
+ if (fds[client].revents & (POLLERR | POLLHUP)) {
+
+ fprintf(stderr, "Session Socket dead\n");
+
+ libwebsocket_close_and_free_session(wsi[client]);
+ goto nuke_this;
+ }
+
+ /* any incoming data ready? */
+
+ if (!(fds[client].revents & POLLIN))
+ continue;
+
+// fprintf(stderr, "POLLIN\n");
+
+ n = recv(fds[client].fd, buf, sizeof(buf), 0);
+ if (n < 0) {
+ fprintf(stderr, "Socket read returned %d\n", n);
+ continue;
+ }
+ if (!n) {
+// fprintf(stderr, "POLLIN with 0 len waiting\n");
+ libwebsocket_close_and_free_session(wsi[client]);
+ goto nuke_this;
+ }
+
+ /* service incoming data */
+
+ if (libwebsocket_read(wsi[client], buf, n) >= 0)
+ continue;
+
+ /* it closed and nuked wsi[client] */
+nuke_this:
+ for (n = client; n < fds_count - 1; n++) {
+ fds[n] = fds[n + 1];
+ wsi[n] = wsi[n + 1];
+ }
+ fds_count--;
+ client--;
}
- /* we are the session process */
+poll_out:
+ for (client = 1; client < fds_count; client++) {
- close(sockfd);
-
- /* sit in libwebsocket_service() until session socket closed */
-
- libwebsocket_service(wsi, sessfd);
+ if (wsi[client]->state != WSI_STATE_ESTABLISHED)
+ continue;
+
+ if (!wsi[client]->callback)
+ continue;
- exit(0);
+ wsi[client]->callback(wsi[client], LWS_CALLBACK_SEND,
+ &wsi[client]->user_space[0], NULL, 0);
+ }
+
+ continue;
}
-}
+
+fatal:
+ close(fds[0].fd);
+ for (client = 1; client < fds_count; client++)
+ libwebsocket_close_and_free_session(wsi[client]);
-static void libwebsocket_close(struct libwebsocket *wsi)
-{
- int n;
-
- wsi->state = WSI_STATE_DEAD_SOCKET;
-
- if (wsi->callback)
- wsi->callback(wsi, LWS_CALLBACK_CLOSED, NULL, 0);
-
- for (n = 0; n < WSI_TOKEN_COUNT; n++)
- if (wsi->utf8_token[n].token)
- free(wsi->utf8_token[n].token);
-
- close(wsi->sock);
+ kill(0, SIGTERM);
+
+ return 0;
}
/**
@@ -518,7 +635,8 @@
return -1;
if (n != len && wsi->callback)
- wsi->callback(wsi, LWS_CALLBACK_RECEIVE, &buf[n], len - n);
+ wsi->callback(wsi, LWS_CALLBACK_RECEIVE, &wsi->user_space[0],
+ &buf[n], len - n);
return -0;
}
@@ -563,6 +681,7 @@
!wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len) {
if (wsi->callback)
(wsi->callback)(wsi, LWS_CALLBACK_HTTP,
+ &wsi->user_space[0],
NULL, 0);
wsi->state = WSI_STATE_HTTP;
return 0;
@@ -674,7 +793,8 @@
/* notify user code that we're ready to roll */
if (wsi->callback)
- wsi->callback(wsi, LWS_CALLBACK_ESTABLISHED, NULL, 0);
+ wsi->callback(wsi, LWS_CALLBACK_ESTABLISHED,
+ &wsi->user_space[0], NULL, 0);
break;
case WSI_STATE_ESTABLISHED:
@@ -688,7 +808,7 @@
return 0;
bail:
- libwebsocket_close(wsi);
+ libwebsocket_close_and_free_session(wsi);
return -1;
}
@@ -826,11 +946,13 @@
}
break;
}
-
-// for (n = 0; n < (len + pre + post); n++)
-// fprintf(stderr, "%02X ", buf[n - pre]);
-//
-// fprintf(stderr, "\n");
+
+#if 0
+ for (n = 0; n < (len + pre + post); n++)
+ fprintf(stderr, "%02X ", buf[n - pre]);
+
+ fprintf(stderr, "\n");
+#endif
send_raw:
@@ -845,63 +967,6 @@
return 0;
}
-static void libwebsocket_service(struct libwebsocket *wsi, int sock)
-{
- int n;
- unsigned char buf[256];
- struct pollfd fds;
-
- wsi->sock = sock;
-
- while (1) {
- fds.fd = sock;
- fds.events = POLLIN;
- fds.revents = 0;
- n = poll(&fds, 1, 50);
-
- if (n < 0) {
- fprintf(stderr, "Socket dead (poll = %d)\n", n);
- return;
- }
- if (n == 0)
- goto pout;
-
- if (fds.revents & (POLLERR | POLLHUP)) {
- fprintf(stderr, "Socket dead\n");
- return;
- }
-
- if (wsi->state == WSI_STATE_DEAD_SOCKET) {
- fprintf(stderr, "Seen socket dead, returning\n");
- return;
- }
-
- if (fds.revents & POLLIN) {
-
-// fprintf(stderr, "POLLIN\n");
-
- n = recv(sock, buf, sizeof(buf), 0);
- if (n < 0) {
- fprintf(stderr, "Socket read returned %d\n", n);
- continue;
- }
- if (n)
- libwebsocket_read(wsi, buf, n);
- else {
- fprintf(stderr, "POLLIN with 0 len waiting\n");
- usleep(50000);
- }
- }
-pout:
- if (wsi->state != WSI_STATE_ESTABLISHED)
- continue;
-
-// fprintf(stderr, "POLLOUT\n");
-
- if (wsi->callback)
- wsi->callback(wsi, LWS_CALLBACK_SEND, NULL, 0);
- }
-}
/**
* libwebsockets_serve_http_file() - Send a file back to the client using http