fork-sever-process-and-introduce-broadcast-api.patch

Signed-off-by: Andy Green <andy@warmcat.com>
This commit is contained in:
Andy Green 2010-12-18 15:13:50 +00:00
parent f2f54d5d26
commit b45993caf3
15 changed files with 4168 additions and 1758 deletions

View file

@ -1,3 +1,2 @@
SUBDIRS=lib test-server
#communal-server

View file

@ -116,6 +116,7 @@ CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DLLTOOL = @DLLTOOL@
DSYMUTIL = @DSYMUTIL@
DUMPBIN = @DUMPBIN@
ECHO_C = @ECHO_C@
@ -139,6 +140,7 @@ LIPO = @LIPO@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
NM = @NM@
NMEDIT = @NMEDIT@
@ -164,6 +166,7 @@ abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_AR = @ac_ct_AR@
ac_ct_CC = @ac_ct_CC@
ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
@ -706,7 +709,6 @@ uninstall-am:
mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
ps ps-am tags tags-recursive uninstall uninstall-am
#communal-server
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.

1088
aclocal.m4 vendored

File diff suppressed because it is too large Load diff

1663
configure vendored

File diff suppressed because it is too large Load diff

View file

@ -2,13 +2,15 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.66])
AC_INIT(libwebsockets, 0.1, andy@warmcat.com)
AC_INIT(libwebsockets, 0.2, andy@warmcat.com)
AC_CONFIG_SRCDIR([test-server/test-server.c])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
LT_INIT(shared)
#AX_PTHREAD
# Checks for programs.
AC_PROG_CC
AC_PROG_INSTALL

View file

@ -6,8 +6,8 @@ dist_libwebsockets_la_SOURCES=libwebsockets.c \
md5.c \
libwebsockets.h \
private-libwebsockets.h
libwebsockets_la_CFLAGS=-rdynamic -fPIC -c
libwebsockets_la_LDFLAGS=-version-info 0:1
libwebsockets_la_CFLAGS=-Wall -Werror -rdynamic -fPIC -c
libwebsockets_la_LDFLAGS=-version-info 0:2
all-local:
../scripts/kernel-doc -html \

View file

@ -111,6 +111,7 @@ CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DLLTOOL = @DLLTOOL@
DSYMUTIL = @DSYMUTIL@
DUMPBIN = @DUMPBIN@
ECHO_C = @ECHO_C@
@ -134,6 +135,7 @@ LIPO = @LIPO@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
NM = @NM@
NMEDIT = @NMEDIT@
@ -159,6 +161,7 @@ abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_AR = @ac_ct_AR@
ac_ct_CC = @ac_ct_CC@
ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
@ -215,8 +218,8 @@ dist_libwebsockets_la_SOURCES = libwebsockets.c \
libwebsockets.h \
private-libwebsockets.h
libwebsockets_la_CFLAGS = -rdynamic -fPIC -c
libwebsockets_la_LDFLAGS = -version-info 0:1
libwebsockets_la_CFLAGS = -Wall -Werror -rdynamic -fPIC -c
libwebsockets_la_LDFLAGS = -version-info 0:2
all: all-am
.SUFFIXES:

View file

@ -54,7 +54,7 @@ libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len);
*
* LWS_CALLBACK_CLOSED: when the websocket session ends
*
* LWS_CALLBACK_SEND: opportunity to send to client (you would use
* LWS_CALLBACK_BROADCAST: signal to send to client (you would use
* libwebsocket_write() taking care about the
* special buffer requirements
* LWS_CALLBACK_RECEIVE: data has appeared for the server, it can be
@ -77,7 +77,12 @@ extern int callback(struct libwebsocket * wsi,
void
libwebsocket_close_and_free_session(struct libwebsocket *wsi)
{
int n = wsi->state;
int n;
if ((unsigned long)wsi < LWS_MAX_PROTOCOLS)
return;
n = wsi->state;
wsi->state = WSI_STATE_DEAD_SOCKET;
@ -110,12 +115,136 @@ libwebsocket_close_and_free_session(struct libwebsocket *wsi)
free(wsi);
}
static int
libwebsocket_poll_connections(struct libwebsocket_context *this)
{
unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + MAX_BROADCAST_PAYLOAD +
LWS_SEND_BUFFER_POST_PADDING];
int client;
int n;
size_t len;
/* check for activity on client sockets */
for (client = this->count_protocols + 1; client < this->fds_count;
client++) {
/* handle session socket closed */
if (this->fds[client].revents & (POLLERR | POLLHUP)) {
debug("Session Socket %d %p (fd=%d) dead\n",
client, this->wsi[client], this->fds[client]);
libwebsocket_close_and_free_session(this->wsi[client]);
goto nuke_this;
}
/* any incoming data ready? */
if (!(this->fds[client].revents & POLLIN))
continue;
/* broadcast? */
if ((unsigned long)this->wsi[client] < LWS_MAX_PROTOCOLS) {
len = read(this->fds[client].fd,
buf + LWS_SEND_BUFFER_PRE_PADDING,
MAX_BROADCAST_PAYLOAD);
if (len < 0) {
fprintf(stderr, "Error receiving broadcast payload\n");
continue;
}
/* broadcast it to all guys with this protocol index */
for (n = this->count_protocols + 1;
n < this->fds_count; n++) {
if ((unsigned long)this->wsi[n] <
LWS_MAX_PROTOCOLS)
continue;
/*
* never broadcast to non-established
* connection
*/
if (this->wsi[n]->state != WSI_STATE_ESTABLISHED)
continue;
/*
* only broadcast to connections using
* the requested protocol
*/
if (this->wsi[n]->protocol->protocol_index !=
(unsigned long)this->wsi[client])
continue;
this->wsi[n]->protocol-> callback(this->wsi[n],
LWS_CALLBACK_BROADCAST,
this->wsi[n]->user_space,
buf + LWS_SEND_BUFFER_PRE_PADDING, len);
}
continue;
}
#ifdef LWS_OPENSSL_SUPPORT
if (this->use_ssl)
n = SSL_read(this->wsi[client]->ssl, buf, sizeof buf);
else
#endif
n = recv(this->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(
this->wsi[client]);
goto nuke_this;
}
/* service incoming data */
if (libwebsocket_read(this->wsi[client], buf, n) >= 0)
continue;
/*
* it closed and nuked wsi[client], so remove the
* socket handle and wsi from our service list
*/
nuke_this:
debug("nuking wsi %p, fsd_count = %d\n",
this->wsi[client], this->fds_count - 1);
this->fds_count--;
for (n = client; n < this->fds_count; n++) {
this->fds[n] = this->fds[n + 1];
this->wsi[n] = this->wsi[n + 1];
}
break;
}
return 0;
}
/**
* libwebsocket_create_server() - Create the listening websockets server
* @port: Port to listen on
* @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
* @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
@ -127,10 +256,10 @@ libwebsocket_close_and_free_session(struct libwebsocket *wsi)
* This function creates the listening socket and takes care
* of all initialization in one step.
*
* It does not return since it sits in a service loop and operates via the
* callbacks given in @protocol. User code should fork before calling
* libwebsocket_create_server() if it wants to do other things in
* parallel other than serve websockets.
* After initialization, it forks a thread that will sits in a service loop
* and returns to the caller. The actual service actions are performed by
* user code in a per-protocol callback from the appropriate one selected
* by the client from the list in @protocols.
*
* The protocol callback functions are called for a handful of events
* including http requests coming in, websocket connections becoming
@ -150,7 +279,7 @@ libwebsocket_close_and_free_session(struct libwebsocket *wsi)
*/
int libwebsocket_create_server(int port,
const struct libwebsocket_protocols *protocols,
struct libwebsocket_protocols *protocols,
const char * ssl_cert_filepath,
const char * ssl_private_key_filepath,
int gid, int uid)
@ -161,11 +290,9 @@ int libwebsocket_create_server(int port,
int fd;
unsigned int clilen;
struct sockaddr_in serv_addr, cli_addr;
struct libwebsocket *wsi[MAX_CLIENTS + 1];
struct pollfd fds[MAX_CLIENTS + 1];
int fds_count = 0;
unsigned char buf[1024];
int opt = 1;
struct libwebsocket_context * this = NULL;
unsigned int slen;
#ifdef LWS_OPENSSL_SUPPORT
SSL_METHOD *method;
@ -195,7 +322,7 @@ int libwebsocket_create_server(int port,
// Firefox insists on SSLv23 not SSLv3
// Konq disables SSLv2 by default now, SSLv23 works
method = SSLv23_server_method(); // create server instance
method = (SSL_METHOD *)SSLv23_server_method();
if (!method) {
fprintf(stderr, "problem creating ssl method: %s\n",
ERR_error_string(ERR_get_error(), ssl_err_buf));
@ -234,6 +361,10 @@ int libwebsocket_create_server(int port,
/* SSL is happy and has a cert it's content with */
}
#endif
this = malloc(sizeof (struct libwebsocket_context));
/* set up our external listening socket we serve on */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
@ -255,7 +386,7 @@ int libwebsocket_create_server(int port,
errno);
return -1;
}
/* drop any root privs for this process */
if (gid != -1)
@ -266,65 +397,198 @@ int libwebsocket_create_server(int port,
fprintf(stderr, "setuid: %s\n", strerror(errno));
/*
* sit there listening for connects, accept and service connections
* in a poll loop, without any forking
* prepare the poll() fd array... it's like this
*
* [0] = external listening socket
* [1 .. this->count_protocols] = per-protocol broadcast sockets
* [this->count_protocols + 1 ... this->fds_count-1] = connection skts
*/
this->fds_count = 1;
this->fds[0].fd = sockfd;
this->fds[0].events = POLLIN;
this->count_protocols = 0;
#ifdef LWS_OPENSSL_SUPPORT
this->use_ssl = use_ssl;
#endif
listen(sockfd, 5);
fprintf(stderr, " Listening on port %d\n", port);
fds[0].fd = sockfd;
fds_count = 1;
fds[0].events = POLLIN;
/* set up our internal broadcast trigger sockets per-protocol */
for (; protocols[this->count_protocols].callback;
this->count_protocols++) {
protocols[this->count_protocols].owning_server = this;
protocols[this->count_protocols].protocol_index =
this->count_protocols;
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
fprintf(stderr, "ERROR opening socket");
return -1;
}
/* allow us to restart even if old sockets in TIME_WAIT */
setsockopt(fd, 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 = 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) {
fprintf(stderr, "ERROR on binding to port %d (%d %d)\n",
port, n, errno);
return -1;
}
slen = sizeof cli_addr;
n = getsockname(fd, (struct sockaddr *)&cli_addr, &slen);
if (n < 0) {
fprintf(stderr, "getsockname failed\n");
return -1;
}
protocols[this->count_protocols].broadcast_socket_port =
ntohs(cli_addr.sin_port);
listen(fd, 5);
debug(" Protocol %s broadcast socket %d\n",
protocols[this->count_protocols].name,
ntohs(cli_addr.sin_port));
this->fds[this->fds_count].fd = fd;
this->fds[this->fds_count].events = POLLIN;
/* wsi only exists for connections, not broadcast listener */
this->wsi[this->fds_count] = NULL;
this->fds_count++;
}
/*
* We will enter out poll and service loop now, just before that
* fork and return to caller for the main thread of execution
*/
n = fork();
if (n < 0) {
fprintf(stderr, "Failed to fork websocket poll loop\n");
return -1;
}
if (n) {
/* original process context */
/*
* before we return to caller, we set up per-protocol
* broadcast sockets connected to the server ready to use
*/
/* give server fork a chance to start up */
sleep(1);
for (client = 1; client < this->count_protocols + 1; client++) {
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
fprintf(stderr,"Unable to create socket\n");
return -1;
}
cli_addr.sin_family = AF_INET;
cli_addr.sin_port = htons(
protocols[client - 1].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) {
fprintf(stderr, "Unable to connect to "
"broadcast socket %d, %s\n",
client, strerror(errno));
return -1;
}
protocols[client - 1].broadcast_socket_user_fd = fd;
}
fprintf(stderr, "libwebsocket poll process forked\n");
return 0;
}
/* we want a SIGHUP when our parent goes down */
prctl(PR_SET_PDEATHSIG, SIGHUP);
/* in this forked process, sit and service websocket connections */
while (1) {
n = poll(fds, fds_count, 50);
if (n < 0 || fds[0].revents & (POLLERR | POLLHUP)) {
n = poll(this->fds, this->fds_count, 1000);
if (n < 0 || this->fds[0].revents & (POLLERR | POLLHUP)) {
fprintf(stderr, "Listen Socket dead\n");
goto fatal;
}
if (n == 0) /* poll timeout */
goto poll_out;
continue;
if (fds[0].revents & POLLIN) {
/* handle accept on listening socket? */
for (client = 0; client < this->count_protocols + 1; client++) {
if (!this->fds[client].revents & POLLIN)
continue;
/* listen socket got an unencrypted connection... */
clilen = sizeof(cli_addr);
fd = accept(sockfd,
(struct sockaddr *)&cli_addr,
&clilen);
fd = accept(this->fds[client].fd,
(struct sockaddr *)&cli_addr, &clilen);
if (fd < 0) {
fprintf(stderr, "ERROR on accept");
continue;
}
if (fds_count >= MAX_CLIENTS) {
if (this->fds_count >= MAX_CLIENTS) {
fprintf(stderr, "too busy");
close(fd);
continue;
}
wsi[fds_count] = malloc(sizeof(struct libwebsocket));
if (!wsi[fds_count])
if (client) {
/*
* accepting a connection to broadcast socket
* set wsi to be protocol index not pointer
*/
this->wsi[this->fds_count] =
(struct libwebsocket *)(long)(client - 1);
goto fill_in_fds;
}
/* accepting connection to main listener */
this->wsi[this->fds_count] =
malloc(sizeof(struct libwebsocket));
if (!this->wsi[this->fds_count])
return -1;
#ifdef LWS_OPENSSL_SUPPORT
if (use_ssl) {
#ifdef LWS_OPENSSL_SUPPORT
if (this->use_ssl) {
wsi[fds_count]->ssl = SSL_new(ssl_ctx);
if (wsi[fds_count]->ssl == NULL) {
this->wsi[this->fds_count]->ssl =
SSL_new(ssl_ctx);
if (this->wsi[this->fds_count]->ssl == NULL) {
fprintf(stderr, "SSL_new failed: %s\n",
ERR_error_string(SSL_get_error(
wsi[fds_count]->ssl, 0), NULL));
free(wsi[fds_count]);
this->wsi[this->fds_count]->ssl, 0),
NULL));
free(this->wsi[this->fds_count]);
continue;
}
SSL_set_fd(wsi[fds_count]->ssl, fd);
SSL_set_fd(this->wsi[this->fds_count]->ssl, fd);
n = SSL_accept(wsi[fds_count]->ssl);
n = SSL_accept(this->wsi[this->fds_count]->ssl);
if (n != 1) {
/*
* browsers seem to probe with various
@ -332,32 +596,37 @@ int libwebsocket_create_server(int port,
* and succeed
*/
debug("SSL_accept failed skt %u: %s\n",
fd,
ERR_error_string(SSL_get_error(
wsi[fds_count]->ssl, n), NULL));
SSL_free(wsi[fds_count]->ssl);
free(wsi[fds_count]);
fd,
ERR_error_string(SSL_get_error(
this->wsi[this->fds_count]->ssl,
n), NULL));
SSL_free(
this->wsi[this->fds_count]->ssl);
free(this->wsi[this->fds_count]);
continue;
}
debug("accepted new SSL conn "
"port %u on fd=%d SSL ver %s\n",
ntohs(cli_addr.sin_port), fd,
SSL_get_version(wsi[fds_count]->ssl));
SSL_get_version(this->wsi[
this->fds_count]->ssl));
} else
#endif
#endif
debug("accepted new conn port %u on fd=%d\n",
ntohs(cli_addr.sin_port), fd);
/* intialize the instance struct */
wsi[fds_count]->sock = fd;
wsi[fds_count]->state = WSI_STATE_HTTP;
wsi[fds_count]->name_buffer_pos = 0;
this->wsi[this->fds_count]->sock = fd;
this->wsi[this->fds_count]->state = WSI_STATE_HTTP;
this->wsi[this->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;
this->wsi[this->fds_count]->
utf8_token[n].token = NULL;
this->wsi[this->fds_count]->
utf8_token[n].token_len = 0;
}
/*
@ -366,8 +635,8 @@ int libwebsocket_create_server(int port,
* to the start of the supported list, so it can look
* for matching ones during the handshake
*/
wsi[fds_count]->protocol = protocols;
wsi[fds_count]->user_space = NULL;
this->wsi[this->fds_count]->protocol = protocols;
this->wsi[this->fds_count]->user_space = NULL;
/*
* Default protocol is 76
@ -375,107 +644,127 @@ int libwebsocket_create_server(int port,
* draft the client wants, when that's seen we modify
* the individual connection's spec revision accordingly
*/
wsi[fds_count]->ietf_spec_revision = 76;
this->wsi[this->fds_count]->ietf_spec_revision = 76;
fds[fds_count].events = POLLIN;
fds[fds_count++].fd = fd;
fill_in_fds:
/*
* make sure NO events are seen yet on this new socket
* (otherwise we inherit old fds[client].revents from
* previous socket there and die mysteriously! )
*/
fds[client].revents = 0;
}
/* check for activity on client sockets */
for (client = 1; client < fds_count; client++) {
/* handle session socket closed */
if (fds[client].revents & (POLLERR | POLLHUP)) {
debug("Session Socket %d %p (fd=%d) dead\n",
client, wsi[client], fds[client]);
this->fds[this->fds_count].revents = 0;
libwebsocket_close_and_free_session(
wsi[client]);
goto nuke_this;
}
/* any incoming data ready? */
this->fds[this->fds_count].events = POLLIN;
this->fds[this->fds_count++].fd = fd;
if (!(fds[client].revents & POLLIN))
continue;
#ifdef LWS_OPENSSL_SUPPORT
if (use_ssl)
n = SSL_read(wsi[client]->ssl, buf, sizeof buf);
else
#endif
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], so remove the
* socket handle and wsi from our service list
*/
nuke_this:
debug("nuking wsi %p, fsd_count = %d\n",
wsi[client], fds_count - 1);
fds_count--;
for (n = client; n < fds_count; n++) {
fds[n] = fds[n + 1];
wsi[n] = wsi[n + 1];
}
break;
}
poll_out:
for (client = 1; client < fds_count; client++) {
if (wsi[client]->state != WSI_STATE_ESTABLISHED)
continue;
/* service anything incoming on websocket connection */
wsi[client]->protocol->callback(wsi[client],
LWS_CALLBACK_SEND,
wsi[client]->user_space,
NULL, 0);
}
continue;
libwebsocket_poll_connections(this);
}
fatal:
/* listening socket */
close(fds[0].fd);
for (client = 1; client < fds_count; client++)
libwebsocket_close_and_free_session(wsi[client]);
/* close listening skt and per-protocol broadcast sockets */
for (client = 0; client < this->fds_count; client++)
close(this->fds[0].fd);
#ifdef LWS_OPENSSL_SUPPORT
SSL_CTX_free(ssl_ctx);
#endif
kill(0, SIGTERM);
if (this)
free(this);
return 0;
}
/**
* 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.
*/
const struct libwebsocket_protocols *
libwebsockets_get_protocol(struct libwebsocket *wsi)
{
return wsi->protocol;
}
/**
* libwebsockets_broadcast() - Sends a buffer to rthe 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 * this = protocol->owning_server;
int n;
if (!protocol->broadcast_socket_user_fd) {
/*
* 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 = this->count_protocols + 1; n < this->fds_count; n++) {
if ((unsigned long)this->wsi[n] < LWS_MAX_PROTOCOLS)
continue;
/* never broadcast to non-established connection */
if (this->wsi[n]->state != WSI_STATE_ESTABLISHED)
continue;
/* only broadcast to guys using requested protocol */
if (this->wsi[n]->protocol != protocol)
continue;
this->wsi[n]->protocol-> callback(this->wsi[n],
LWS_CALLBACK_BROADCAST,
this->wsi[n]->user_space,
buf, len);
}
return 0;
}
n = send(protocol->broadcast_socket_user_fd, buf, len, 0);
return n;
}

View file

@ -26,9 +26,9 @@
enum libwebsocket_callback_reasons {
LWS_CALLBACK_ESTABLISHED,
LWS_CALLBACK_CLOSED,
LWS_CALLBACK_SEND,
LWS_CALLBACK_RECEIVE,
LWS_CALLBACK_HTTP,
LWS_CALLBACK_BROADCAST,
};
enum libwebsocket_write_protocol {
@ -38,6 +38,7 @@ enum libwebsocket_write_protocol {
};
struct libwebsocket;
struct libwebsocket_context;
/**
* struct libwebsocket_protocols - List of protocols and handlers server
@ -51,6 +52,16 @@ struct libwebsocket;
* this much memory allocated on connection establishment and
* freed on connection takedown. A pointer to this per-connection
* allocation is passed into the callback in the 'user' parameter
* @owning_server: the server init call fills in this opaque pointer when
* registering this protocol with the server.
* @broadcast_socket_port: the server init call fills this in with the
* localhost port number used to forward broadcasts for this
* protocol
* @broadcast_socket_user_fd: the server init call fills this in ... the main()
* process context can write to this socket to perform broadcasts
* (use the libwebsockets_broadcast() api to do this instead,
* it works from any process context)
* @protocol_index: which protocol we are starting from zero
*
* This structure represents one protocol supported by the server. An
* array of these structures is passed to libwebsocket_create_server()
@ -63,10 +74,20 @@ struct libwebsocket_protocols {
enum libwebsocket_callback_reasons reason, void *user,
void *in, size_t len);
size_t per_session_data_size;
/*
* below are filled in on server init and can be left uninitialized,
* no need for user to use them directly either
*/
struct libwebsocket_context *owning_server;
int broadcast_socket_port;
int broadcast_socket_user_fd;
int protocol_index;
};
extern int libwebsocket_create_server(int port,
const struct libwebsocket_protocols *protocols,
struct libwebsocket_protocols *protocols,
const char *ssl_cert_filepath,
const char *ssl_private_key_filepath, int gid, int uid);
@ -105,4 +126,13 @@ extern int
libwebsockets_serve_http_file(struct libwebsocket *wsi, const char *file,
const char *content_type);
/* notice - you need the pre- and post- padding allocation for buf below */
extern int
libwebsockets_broadcast(const struct libwebsocket_protocols * protocol,
unsigned char *buf, size_t len);
extern const struct libwebsocket_protocols *
libwebsockets_get_protocol(struct libwebsocket *wsi);
#endif

View file

@ -32,7 +32,9 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/prctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>
#include <sys/mman.h>
@ -45,7 +47,7 @@
#include "libwebsockets.h"
/* #define DEBUG */
/* #define DEBUG */
#ifdef DEBUG
@ -67,7 +69,8 @@ extern int use_ssl;
#define LWS_INITIAL_HDR_ALLOC 256
#define LWS_ADDITIONAL_HDR_ALLOC 64
#define MAX_USER_RX_BUFFER 512
#define MAX_BROADCAST_PAYLOAD 1024
#define LWS_MAX_PROTOCOLS 10
enum lws_connection_states {
WSI_STATE_HTTP,
@ -113,6 +116,16 @@ struct lws_tokens {
int token_len;
};
struct libwebsocket_context {
struct libwebsocket *wsi[MAX_CLIENTS + 1];
struct pollfd fds[MAX_CLIENTS + 1];
int fds_count;
#ifdef LWS_OPENSSL_SUPPORT
int use_ssl;
#endif
int count_protocols;
};
/*
* This is totally opaque to code using the library. It's exported as a
@ -152,3 +165,10 @@ libwebsocket_close_and_free_session(struct libwebsocket *wsi);
extern void
libwebsockets_md5(const unsigned char *input, int ilen, unsigned char *output);
extern int
libwebsocket_parse(struct libwebsocket *wsi, unsigned char c);
extern int
libwebsocket_interpret_incoming_packet(struct libwebsocket *wsi,
unsigned char *buf, size_t len);

View file

@ -39,9 +39,9 @@ after successful websocket handshake
<blockquote>
when the websocket session ends
</blockquote>
<h3>LWS_CALLBACK_SEND</h3>
<h3>LWS_CALLBACK_BROADCAST</h3>
<blockquote>
opportunity to send to client (you would use
signal to send to client (you would use
<b>libwebsocket_write</b> taking care about the
special buffer requirements
</blockquote>
@ -66,7 +66,7 @@ simple to send back a file to the client.
<i>int</i>
<b>libwebsocket_create_server</b>
(<i>int</i> <b>port</b>,
<i>const struct libwebsocket_protocols *</i> <b>protocols</b>,
<i>struct libwebsocket_protocols *</i> <b>protocols</b>,
<i>const char *</i> <b>ssl_cert_filepath</b>,
<i>const char *</i> <b>ssl_private_key_filepath</b>,
<i>int</i> <b>gid</b>,
@ -79,6 +79,7 @@ simple to send back a file to the client.
<dd>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
<dt><b>ssl_cert_filepath</b>
<dd>If libwebsockets was compiled to use ssl, and you want
to listen using SSL, set to the filepath to fetch the
@ -96,10 +97,10 @@ else ignored
This function creates the listening socket and takes care
of all initialization in one step.
<p>
It does not return since it sits in a service loop and operates via the
callbacks given in <tt><b>protocol</b></tt>. User code should fork before calling
<b>libwebsocket_create_server</b> if it wants to do other things in
parallel other than serve websockets.
After initialization, it forks a thread that will sits in a service loop
and returns to the caller. The actual service actions are performed by
user code in a per-protocol callback from the appropriate one selected
by the client from the list in <tt><b>protocols</b></tt>.
<p>
The protocol callback functions are called for a handful of events
including http requests coming in, websocket connections becoming
@ -118,6 +119,53 @@ images or whatever over http and dynamic data over websockets all in
one place; they're all handled in the user callback.
</blockquote>
<hr>
<h2>libwebsockets_get_protocol - Returns a protocol pointer from a websocket connection.</h2>
<i>const struct libwebsocket_protocols *</i>
<b>libwebsockets_get_protocol</b>
(<i>struct libwebsocket *</i> <b>wsi</b>)
<h3>Arguments</h3>
<dl>
<dt><b>wsi</b>
<dd>pointer to struct websocket you want to know the protocol of
</dl>
<h3>Description</h3>
<blockquote>
<p>
This is useful to get the protocol to broadcast back to from inside
the callback.
</blockquote>
<hr>
<h2>libwebsockets_broadcast - Sends a buffer to rthe callback for all active connections of the given protocol.</h2>
<i>int</i>
<b>libwebsockets_broadcast</b>
(<i>const struct libwebsocket_protocols *</i> <b>protocol</b>,
<i>unsigned char *</i> <b>buf</b>,
<i>size_t</i> <b>len</b>)
<h3>Arguments</h3>
<dl>
<dt><b>protocol</b>
<dd>pointer to the protocol you will broadcast to all members of
<dt><b>buf</b>
<dd>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.
<dt><b>len</b>
<dd>length of payload data in buf, starting from buf.
</dl>
<h3>Description</h3>
<blockquote>
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 <b>libwebsocket_write</b>.
<p>
<b>libwebsockets_broadcast</b> can be called from another fork context without
having to take any care about data visibility between the processes, it'll
"just work".
</blockquote>
<hr>
<h2>libwebsocket_write - Apply protocol then write data to client</h2>
<i>int</i>
<b>libwebsocket_write</b>
@ -183,6 +231,10 @@ local files down the http link in a single step.
&nbsp; &nbsp; <i>const char *</i> <b>name</b>;<br>
&nbsp; &nbsp; <i>int (*</i><b>callback</b>) <i>(struct libwebsocket *wsi,enum libwebsocket_callback_reasons reason, void *user,void *in, size_t len)</i>;<br>
&nbsp; &nbsp; <i>size_t</i> <b>per_session_data_size</b>;<br>
&nbsp; &nbsp; <i>struct libwebsocket_context *</i> <b>owning_server</b>;<br>
&nbsp; &nbsp; <i>int</i> <b>broadcast_socket_port</b>;<br>
&nbsp; &nbsp; <i>int</i> <b>broadcast_socket_user_fd</b>;<br>
&nbsp; &nbsp; <i>int</i> <b>protocol_index</b>;<br>
};<br>
<h3>Members</h3>
<dl>
@ -198,6 +250,20 @@ the protocol-specific callback
this much memory allocated on connection establishment and
freed on connection takedown. A pointer to this per-connection
allocation is passed into the callback in the 'user' parameter
<dt><b>owning_server</b>
<dd>the server init call fills in this opaque pointer when
registering this protocol with the server.
<dt><b>broadcast_socket_port</b>
<dd>the server init call fills this in with the
localhost port number used to forward broadcasts for this
protocol
<dt><b>broadcast_socket_user_fd</b>
<dd>the server init call fills this in ... the <b>main</b>
process context can write to this socket to perform broadcasts
(use the <b>libwebsockets_broadcast</b> api to do this instead,
it works from any process context)
<dt><b>protocol_index</b>
<dd>which protocol we are starting from zero
</dl>
<h3>Description</h3>
<blockquote>

View file

@ -1,6 +1,6 @@
Name: libwebsockets
Version: 0.1
Release: 26.gmaster_6d972248%{?dist}
Release: 31.gmaster_f2f54d5d%{?dist}
Summary: Websocket Server Library
Group: System

2371
ltmain.sh

File diff suppressed because it is too large Load diff

View file

@ -84,6 +84,7 @@ CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DLLTOOL = @DLLTOOL@
DSYMUTIL = @DSYMUTIL@
DUMPBIN = @DUMPBIN@
ECHO_C = @ECHO_C@
@ -107,6 +108,7 @@ LIPO = @LIPO@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
NM = @NM@
NMEDIT = @NMEDIT@
@ -132,6 +134,7 @@ abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_AR = @ac_ct_AR@
ac_ct_CC = @ac_ct_CC@
ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@

View file

@ -101,7 +101,13 @@ callback_dumb_increment(struct libwebsocket *wsi,
pss->number = 0;
break;
case LWS_CALLBACK_SEND:
/*
* in this protocol, we just use the broadcast action as the chance to
* send our own connection-specific data and ignore the broadcast info
* that is available in the 'in' parameter
*/
case LWS_CALLBACK_BROADCAST:
n = sprintf(p, "%d", pss->number++);
n = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
if (n < 0) {
@ -162,41 +168,24 @@ callback_lws_mirror(struct libwebsocket *wsi,
pss->ringbuffer_tail = ringbuffer_head;
break;
case LWS_CALLBACK_SEND:
/* send everything that's pending */
while (pss->ringbuffer_tail != ringbuffer_head) {
n = libwebsocket_write(wsi, (unsigned char *)
ringbuffer[pss->ringbuffer_tail].payload +
LWS_SEND_BUFFER_PRE_PADDING,
ringbuffer[pss->ringbuffer_tail].len,
LWS_WRITE_TEXT);
if (n < 0) {
fprintf(stderr, "ERROR writing to socket");
exit(1);
}
if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
pss->ringbuffer_tail = 0;
else
pss->ringbuffer_tail++;
}
case LWS_CALLBACK_BROADCAST:
n = libwebsocket_write(wsi, in, len, LWS_WRITE_TEXT);
break;
case LWS_CALLBACK_RECEIVE:
if (ringbuffer[ringbuffer_head].payload)
free(ringbuffer[ringbuffer_head].payload);
ringbuffer[ringbuffer_head].payload =
malloc(LWS_SEND_BUFFER_PRE_PADDING + len +
LWS_SEND_BUFFER_POST_PADDING);
ringbuffer[ringbuffer_head].len = len;
memcpy(ringbuffer[ringbuffer_head].payload +
LWS_SEND_BUFFER_PRE_PADDING, in, len);
if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
ringbuffer_head = 0;
else
ringbuffer_head++;
/*
* copy the incoming packet to all other protocol users
*
* This demonstrates how easy it is to broadcast from inside
* a callback.
*
* How this works is it calls back to the callback for all
* connected sockets using this protocol with
* LWS_CALLBACK_BROADCAST reason. Our handler for that above
* writes the data down the socket.
*/
libwebsockets_broadcast(libwebsockets_get_protocol(wsi),
in, len);
break;
default:
@ -209,7 +198,7 @@ callback_lws_mirror(struct libwebsocket *wsi,
/* list of supported protocols and callbacks */
static const struct libwebsocket_protocols protocols[] = {
static struct libwebsocket_protocols protocols[] = {
{
.name = "http-only",
.callback = callback_http,
@ -246,6 +235,8 @@ int main(int argc, char **argv)
LOCAL_RESOURCE_PATH"/libwebsockets-test-server.pem";
const char *key_path =
LOCAL_RESOURCE_PATH"/libwebsockets-test-server.key.pem";
unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 1024 +
LWS_SEND_BUFFER_POST_PADDING];
fprintf(stderr, "libwebsockets test server\n"
"(C) Copyright 2010 Andy Green <andy@warmcat.com> "
@ -272,11 +263,40 @@ int main(int argc, char **argv)
if (!use_ssl)
cert_path = key_path = NULL;
if (libwebsocket_create_server(port, protocols,
cert_path, key_path, -1, -1) < 0) {
if (libwebsocket_create_server(port, protocols, cert_path, key_path,
-1, -1) < 0) {
fprintf(stderr, "libwebsocket init failed\n");
return -1;
}
/*
* After initializing and creating the websocket server in its own fork
* we return to the main process here
*/
buf[LWS_SEND_BUFFER_PRE_PADDING] = 'x';
while (1) {
sleep(1);
/*
* This broadcasts to all dumb-increment-protocol connections
* once per second.
*
* We're just sending a character 'x', in these examples the
* callbacks send their own per-connection content.
*
* You have to send something with nonzero length to get the
* callback actions delivered.
*
* We take care of pre-and-post padding allocation.
*/
/* [1] == dumb-increment-protocol */
libwebsockets_broadcast(&protocols[1],
&buf[LWS_SEND_BUFFER_PRE_PADDING], 1);
}
return 0;
}