From 90c7cbcc0024d42475984b0c11f78b575292b529 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Thu, 27 Jan 2011 06:26:52 +0000 Subject: [PATCH] introduce-ssl-client-connections--flow-control.patch Signed-off-by: Andy Green --- Makefile.am | 2 +- Makefile.in | 3 +- README-test-server | 26 ++++- configure | 24 ++++- configure.ac | 16 ++- lib/Makefile.am | 2 +- lib/Makefile.in | 3 +- lib/client-handshake.c | 111 +++++++++++++++++--- lib/handshake.c | 2 +- lib/libwebsockets.c | 250 ++++++++++++++++++++++++++++++++++---------- lib/libwebsockets.h | 41 +++++++- lib/parsers.c | 25 ++--- lib/private-libwebsockets.h | 10 +- lib/sha-1.c | 16 +-- libwebsockets-api-doc.html | 105 ++++++++++++++++++- test-server/Makefile.am | 6 ++ test-server/Makefile.in | 54 ++++++++-- test-server/test-client.c | 240 +++++++++--------------------------------- test-server/test-server.c | 134 ++++++++++++++++++------ test-server/test.html | 33 ++++-- 20 files changed, 755 insertions(+), 348 deletions(-) diff --git a/Makefile.am b/Makefile.am index bb67621..dec9704 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,2 +1,2 @@ -SUBDIRS=lib test-server +SUBDIRS=lib test-server diff --git a/Makefile.in b/Makefile.in index 02a4a84..20e9dba 100644 --- a/Makefile.in +++ b/Makefile.in @@ -181,6 +181,7 @@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ +clientcertdir = @clientcertdir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ @@ -214,7 +215,7 @@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ -SUBDIRS = lib test-server +SUBDIRS = lib test-server all: config.h $(MAKE) $(AM_MAKEFLAGS) all-recursive diff --git a/README-test-server b/README-test-server index 9a48db2..f10520e 100644 --- a/README-test-server +++ b/README-test-server @@ -33,6 +33,10 @@ There are a couple of other possible configure options to disable the built-in ones and force use of the libcrypto (part of openssl) ones. +--with-client-cert-dir=dir tells the client ssl support where to + look for trust certificates to validate + the remote certificate against. + Testing server with a browser ----------------------------- @@ -44,11 +48,8 @@ It will fetch a script in the form of test.html, and then run the script in there on the browser to open a websocket connection. Incrementing numbers should appear in the browser display. -Using SSL ---------- - -The client side operation does not support SSL yet, but the -server side does. +Using SSL on the server side +---------------------------- To test it using SSL/WSS, just run the test server with @@ -68,6 +69,7 @@ same. test-server.c is all that is needed to use libwebsockets for serving both the script html over http and websockets. + Forkless operation ------------------ @@ -76,6 +78,7 @@ libwebsockets from your own main loop instead. Use the configure option --nofork and simply call libwebsocket_service() from your own main loop as shown in the test app sources. + Testing websocket client support -------------------------------- @@ -91,6 +94,19 @@ if you connect to the test server using a browser at the same time you will be able to see the circles being drawn. +Testing SSL on the client side +------------------------------ + +To test SSL/WSS client action, just run the client test with + +$ libwebsockets-test-client localhost --ssl + +By default the client test applet is set to accept selfsigned +certificates used by the test server, this is indicated by the +use_ssl var being set to 2. Set it to 1 to reject any server +certificate that it doesn't have a trusted CA cert for. + + Websocket version supported --------------------------- diff --git a/configure b/configure index 3e0ce28..4ff42ac 100755 --- a/configure +++ b/configure @@ -616,6 +616,7 @@ ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS LIBOBJS +clientcertdir LIBCRYPTO_FALSE LIBCRYPTO_TRUE CPP @@ -739,6 +740,7 @@ enable_libtool_lock enable_openssl enable_nofork enable_libcrypto +with_client_cert_dir ' ac_precious_vars='build_alias host_alias @@ -1383,7 +1385,7 @@ Optional Features: --disable-libtool-lock avoid locking (might break parallel builds) --enable-openssl Enables https support and needs openssl libs --enable-nofork Disables fork-related options - --enable-libcrypto Use libcrypto MD5 and SHA1 implemntations + --enable-libcrypto Use libcrypto MD5 and SHA1 implementations Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] @@ -1393,6 +1395,8 @@ Optional Packages: --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-sysroot=DIR Search for dependent libraries within DIR (or the compiler's sysroot if not specified). + --with-client-cert-dir directory containing client certs, defaults to + /etc/pki/tls/certs/ Some influential environment variables: CC C compiler command @@ -11963,6 +11967,7 @@ fi + # # # @@ -12285,6 +12290,23 @@ fi +# +# +# + +# Check whether --with-client-cert-dir was given. +if test "${with_client_cert_dir+set}" = set; then : + withval=$with_client_cert_dir; clientcertdir=$withval +else + clientcertdir=/etc/pki/tls/certs/ + +fi + + + + + + # Checks for header files. for ac_header in fcntl.h netinet/in.h stdlib.h string.h sys/socket.h unistd.h diff --git a/configure.ac b/configure.ac index 019b3f9..00b69f4 100644 --- a/configure.ac +++ b/configure.ac @@ -17,6 +17,7 @@ AC_PROG_INSTALL AC_PROG_MAKE_SET AC_CONFIG_MACRO_DIR([m4]) + # # # @@ -48,7 +49,7 @@ fi # # AC_ARG_ENABLE(libcrypto, - [ --enable-libcrypto Use libcrypto MD5 and SHA1 implemntations], + [ --enable-libcrypto Use libcrypto MD5 and SHA1 implementations], [ libcrypto=yes ]) @@ -59,6 +60,19 @@ fi AM_CONDITIONAL(LIBCRYPTO, test x$libcrypto = xyes) +# +# +# +AC_ARG_WITH([client-cert-dir], +[AS_HELP_STRING([--with-client-cert-dir],[directory containing client certs, defaults to /etc/pki/tls/certs/])], +[clientcertdir=$withval], +[clientcertdir=/etc/pki/tls/certs/] +) +AC_SUBST([clientcertdir]) + +AC_SUBST([CFLAGS]) + + # Checks for header files. AC_CHECK_HEADERS([fcntl.h netinet/in.h stdlib.h string.h sys/socket.h unistd.h]) diff --git a/lib/Makefile.am b/lib/Makefile.am index 2280bdc..8328c29 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -12,7 +12,7 @@ else dist_libwebsockets_la_SOURCES += md5.c sha-1.c endif -libwebsockets_la_CFLAGS=-Wall -Werror -std=gnu99 -pedantic -rdynamic -fPIC -c +libwebsockets_la_CFLAGS:=-rdynamic -fPIC -Wall -Werror -std=gnu99 -pedantic -c -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\" libwebsockets_la_LDFLAGS=-version-info 0:2 all-local: diff --git a/lib/Makefile.in b/lib/Makefile.in index 9f58996..775baba 100644 --- a/lib/Makefile.in +++ b/lib/Makefile.in @@ -183,6 +183,7 @@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ +clientcertdir = @clientcertdir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ @@ -221,7 +222,7 @@ include_HEADERS = libwebsockets.h dist_libwebsockets_la_SOURCES = libwebsockets.c handshake.c parsers.c \ libwebsockets.h base64-decode.c client-handshake.c \ private-libwebsockets.h $(am__append_1) -libwebsockets_la_CFLAGS = -Wall -Werror -std=gnu99 -pedantic -rdynamic -fPIC -c +libwebsockets_la_CFLAGS := -rdynamic -fPIC -Wall -Werror -std=gnu99 -pedantic -c -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\" libwebsockets_la_LDFLAGS = -version-info 0:2 all: all-am diff --git a/lib/client-handshake.c b/lib/client-handshake.c index 64f72cb..e93ce99 100644 --- a/lib/client-handshake.c +++ b/lib/client-handshake.c @@ -9,8 +9,10 @@ void strtolower(char *s) { - while (*s) - *s++ = tolower(*s); + while (*s) { + *s = tolower(*s); + s++; + } } void @@ -53,7 +55,7 @@ libwebsocket_client_close(struct libwebsocket *wsi) /* shut down reasonably cleanly */ #ifdef LWS_OPENSSL_SUPPORT - if (use_ssl) { + if (wsi->ssl) { n = SSL_get_fd(wsi->ssl); SSL_shutdown(wsi->ssl); close(n); @@ -67,10 +69,29 @@ libwebsocket_client_close(struct libwebsocket *wsi) #endif } + +/** + * libwebsocket_client_connect() - Connect to another websocket server + * @this: Websocket context + * @address: Remote server address, eg, "myserver.com" + * @port: Port to connect to on the remote server, eg, 80 + * @ssl_connection: 0 = ws://, 1 = wss:// encrypted, 2 = wss:// allow self + * signed certs + * @path: Websocket path on server + * @host: Hostname on server + * @origin: Socket origin name + * @protocol: Comma-separated list of protocols being asked for from + * the server, or just one. The server will pick the one it + * likes best. + * + * This function creates a connection to a remote server + */ + struct libwebsocket * -libwebsocket_client_connect(struct libwebsocket_context *clients, +libwebsocket_client_connect(struct libwebsocket_context *this, const char *address, int port, + int ssl_connection, const char *path, const char *host, const char *origin, @@ -94,12 +115,22 @@ libwebsocket_client_connect(struct libwebsocket_context *clients, int okay = 0; struct libwebsocket *wsi; int n; +#ifdef LWS_OPENSSL_SUPPORT + char ssl_err_buf[512]; +#else + if (ssl_connection) { + fprintf(stderr, "libwebsockets not configured for ssl\n"); + return NULL; + } +#endif wsi = malloc(sizeof(struct libwebsocket)); - if (wsi == NULL) + if (wsi == NULL) { + fprintf(stderr, "Out of memort allocing new connection\n"); return NULL; + } - clients->wsi[clients->fds_count] = wsi; + this->wsi[this->fds_count] = wsi; wsi->ietf_spec_revision = 4; wsi->name_buffer_pos = 0; @@ -137,10 +168,36 @@ libwebsocket_client_connect(struct libwebsocket_context *clients, if (connect(wsi->sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) { - fprintf(stderr, "Connect failed"); + fprintf(stderr, "Connect failed\n"); goto bail1; } +#ifdef LWS_OPENSSL_SUPPORT + if (ssl_connection) { + + wsi->ssl = SSL_new(this->ssl_client_ctx); + wsi->client_bio = BIO_new_socket(wsi->sock, BIO_NOCLOSE); + SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio); + + if (SSL_connect(wsi->ssl) <= 0) { + fprintf(stderr, "SSL connect error %s\n", + ERR_error_string(ERR_get_error(), ssl_err_buf)); + goto bail2; + } + + n = SSL_get_verify_result(wsi->ssl); + if (n != X509_V_OK) { + if (n == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && + ssl_connection == 2) + goto cert_okay; + + fprintf(stderr, "server's cert didn't look good %d\n", n); + goto bail2; + } + } else + wsi->ssl = NULL; +cert_okay: +#endif /* * create the random key */ @@ -199,7 +256,17 @@ libwebsocket_client_connect(struct libwebsocket_context *clients, /* send our request to the server */ - n = send(wsi->sock, pkt, p - pkt, 0); +#ifdef LWS_OPENSSL_SUPPORT + if (ssl_connection) + n = SSL_write(wsi->ssl, pkt, p - pkt); + else +#endif + n = send(wsi->sock, pkt, p - pkt, 0); + + if (n < 0) { + fprintf(stderr, "ERROR writing to client socket\n"); + goto bail2; + } wsi->parser_state = WSI_TOKEN_NAME_PART; @@ -230,7 +297,13 @@ libwebsocket_client_connect(struct libwebsocket_context *clients, * Sec-WebSocket-Protocol: chat */ - len = recv(wsi->sock, pkt, sizeof pkt, 0); +#ifdef LWS_OPENSSL_SUPPORT + if (ssl_connection) + len = SSL_read(wsi->ssl, pkt, sizeof pkt); + else +#endif + len = recv(wsi->sock, pkt, sizeof pkt, 0); + if (len < 0) { fprintf(stderr, "libwebsocket_client_handshake read error\n"); goto bail2; @@ -300,7 +373,7 @@ libwebsocket_client_connect(struct libwebsocket_context *clients, if (!wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len) { /* no protocol name to work from, default to first protocol */ - wsi->protocol = &clients->protocols[0]; + wsi->protocol = &this->protocols[0]; goto check_accept; } @@ -331,10 +404,10 @@ libwebsocket_client_connect(struct libwebsocket_context *clients, */ n = 0; wsi->protocol = NULL; - while (clients->protocols[n].callback) { + while (this->protocols[n].callback) { if (strcmp(wsi->utf8_token[WSI_TOKEN_PROTOCOL].token, - clients->protocols[n].name) == 0) - wsi->protocol = &clients->protocols[n]; + this->protocols[n].name) == 0) + wsi->protocol = &this->protocols[n]; n++; } @@ -373,15 +446,21 @@ check_accept: /* okay he is good to go */ - clients->fds[clients->fds_count].fd = wsi->sock; - clients->fds[clients->fds_count].revents = 0; - clients->fds[clients->fds_count++].events = POLLIN; + this->fds[this->fds_count].fd = wsi->sock; + this->fds[this->fds_count].revents = 0; + this->fds[this->fds_count++].events = POLLIN; wsi->state = WSI_STATE_ESTABLISHED; wsi->client_mode = 1; fprintf(stderr, "handshake OK for protocol %s\n", wsi->protocol->name); + /* call him back to inform him he is up */ + + wsi->protocol->callback(wsi, + LWS_CALLBACK_CLIENT_ESTABLISHED, + wsi->user_space, + NULL, 0); return wsi; diff --git a/lib/handshake.c b/lib/handshake.c index d6928d7..d126809 100644 --- a/lib/handshake.c +++ b/lib/handshake.c @@ -131,7 +131,7 @@ handshake_00(struct libwebsocket *wsi) strcpy(p, wsi->utf8_token[WSI_TOKEN_ORIGIN].token); p += wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len; #ifdef LWS_OPENSSL_SUPPORT - if (use_ssl) { + if (wsi->ssl) { strcpy(p, "\x0d\x0aSec-WebSocket-Location: wss://"); p += strlen("\x0d\x0aSec-WebSocket-Location: wss://"); } else { diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index 1c4bd17..8d46209 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -21,11 +21,6 @@ #include "private-libwebsockets.h" -#ifdef LWS_OPENSSL_SUPPORT -SSL_CTX *ssl_ctx; -int use_ssl; -#endif - void libwebsocket_close_and_free_session(struct libwebsocket *wsi) { @@ -49,7 +44,7 @@ libwebsocket_close_and_free_session(struct libwebsocket *wsi) /* fprintf(stderr, "closing fd=%d\n", wsi->sock); */ #ifdef LWS_OPENSSL_SUPPORT - if (use_ssl) { + if (wsi->ssl) { n = SSL_get_fd(wsi->ssl); SSL_shutdown(wsi->ssl); close(n); @@ -93,6 +88,19 @@ libwebsocket_poll_connections(struct libwebsocket_context *this) goto nuke_this; } + /* the guy requested a callback when it was OK to write */ + + if ((unsigned long)this->wsi[client] > LWS_MAX_PROTOCOLS && + this->fds[client].revents & POLLOUT) { + + this->fds[client].events &= ~POLLOUT; + + this->wsi[client]->protocol->callback(this->wsi[client], + LWS_CALLBACK_CLIENT_WRITEABLE, + this->wsi[client]->user_space, + NULL, 0); + } + /* any incoming data ready? */ if (!(this->fds[client].revents & POLLIN)) @@ -158,7 +166,7 @@ libwebsocket_poll_connections(struct libwebsocket_context *this) } #ifdef LWS_OPENSSL_SUPPORT - if (this->use_ssl) + if (this->wsi[client]->ssl) n = SSL_read(this->wsi[client]->ssl, buf, sizeof buf); else #endif @@ -218,8 +226,8 @@ libwebsocket_context_destroy(struct libwebsocket_context *this) libwebsocket_close_and_free_session(this->wsi[client]); #ifdef LWS_OPENSSL_SUPPORT - if (ssl_ctx) - SSL_CTX_free(ssl_ctx); + if (this->ssl_ctx) + SSL_CTX_free(this->ssl_ctx); #endif if (this) @@ -336,9 +344,12 @@ libwebsocket_service(struct libwebsocket_context *this, int timeout_ms) } #ifdef LWS_OPENSSL_SUPPORT + this->wsi[this->fds_count]->ssl = NULL; + if (this->use_ssl) { - this->wsi[this->fds_count]->ssl = SSL_new(ssl_ctx); + this->wsi[this->fds_count]->ssl = + SSL_new(this->ssl_ctx); if (this->wsi[this->fds_count]->ssl == NULL) { fprintf(stderr, "SSL_new failed: %s\n", ERR_error_string(SSL_get_error( @@ -440,7 +451,8 @@ fatal: close(this->fds[0].fd); #ifdef LWS_OPENSSL_SUPPORT - SSL_CTX_free(ssl_ctx); + if (this->ssl_ctx) + SSL_CTX_free(this->ssl_ctx); #endif if (this) @@ -453,6 +465,91 @@ fatal: return 1; } +/** + * libwebsocket_callback_on_writable() - Request a callback when this socket + * becomes able to be written to without + * blocking + * + * @wsi: Websocket connection instance to get callback for + */ + +int +libwebsocket_callback_on_writable(struct libwebsocket *wsi) +{ + struct libwebsocket_context *this = wsi->protocol->owning_server; + int n; + + for (n = this->count_protocols + 1; n < this->fds_count; n++) + if (this->wsi[n] == wsi) { + this->fds[n].events |= POLLOUT; + return 0; + } + + fprintf(stderr, "libwebsocket_callback_on_writable " + "didn't find socket\n"); + return 1; +} + +/** + * libwebsocket_callback_on_writable_all_protocol() - Request a callback for + * all connections using the given protocol when it + * becomes possible to write to each socket without + * blocking in turn. + * + * @protocol: Protocol whose connections will get callbacks + */ + +int +libwebsocket_callback_on_writable_all_protocol( + const struct libwebsocket_protocols *protocol) +{ + struct libwebsocket_context *this = protocol->owning_server; + int n; + + for (n = this->count_protocols + 1; n < this->fds_count; n++) + if ((unsigned long)this->wsi[n] > LWS_MAX_PROTOCOLS) + if (this->wsi[n]->protocol == protocol) + this->fds[n].events |= POLLOUT; + + return 0; +} + +/** + * libwebsocket_rx_flow_control() - Enable and disable socket servicing for + * receieved packets. + * + * If the output side of a server process becomes choked, this allows flow + * control for the input side. + * + * @wsi: Websocket connection instance to get callback for + * @enable: 0 = disable read servicing for this connection, 1 = enable + */ + +int +libwebsocket_rx_flow_control(struct libwebsocket *wsi, int enable) +{ + struct libwebsocket_context *this = wsi->protocol->owning_server; + int n; + + for (n = this->count_protocols + 1; n < this->fds_count; n++) + if (this->wsi[n] == wsi) { + if (enable) + this->fds[n].events |= POLLIN; + else + this->fds[n].events &= ~POLLIN; + + return 0; + } + + fprintf(stderr, "libwebsocket_callback_on_writable " + "unable to find socket\n"); + return 1; +} + +static void sigpipe_handler(int x) +{ +} + /** * libwebsocket_create_context() - Create the websocket handler @@ -515,47 +612,103 @@ libwebsocket_create_context(int port, #ifdef LWS_OPENSSL_SUPPORT SSL_METHOD *method; char ssl_err_buf[512]; +#endif - use_ssl = ssl_cert_filepath != NULL && ssl_private_key_filepath != NULL; - if (use_ssl) - fprintf(stderr, " Compiled with SSL support, using it\n"); - else - fprintf(stderr, " Compiled with SSL support, not using it\n"); - -#else - if (ssl_cert_filepath != NULL && ssl_private_key_filepath != NULL) { - fprintf(stderr, " Not compiled for OpenSSl support!\n"); + this = malloc(sizeof(struct libwebsocket_context)); + if (!this) { + fprintf(stderr, "No memory for websocket context\n"); return NULL; } - fprintf(stderr, " Compiled without SSL support, serving unencrypted\n"); + this->protocols = protocols; + this->listen_port = port; + + if (port) { + +#ifdef LWS_OPENSSL_SUPPORT + this->use_ssl = ssl_cert_filepath != NULL && + ssl_private_key_filepath != NULL; + if (this->use_ssl) + fprintf(stderr, " Compiled with SSL support, " + "using it\n"); + else + fprintf(stderr, " Compiled with SSL support, " + "not using it\n"); + +#else + if (ssl_cert_filepath != NULL && + ssl_private_key_filepath != NULL) { + fprintf(stderr, " Not compiled for OpenSSl support!\n"); + return NULL; + } + fprintf(stderr, " Compiled without SSL support, " + "serving unencrypted\n"); #endif + } + + /* ignore SIGPIPE */ + + signal(SIGPIPE, sigpipe_handler); + #ifdef LWS_OPENSSL_SUPPORT - if (use_ssl) { - SSL_library_init(); - OpenSSL_add_all_algorithms(); - SSL_load_error_strings(); + /* basic openssl init */ - /* - * Firefox insists on SSLv23 not SSLv3 - * Konq disables SSLv2 by default now, SSLv23 works - */ + SSL_library_init(); + + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + + /* + * Firefox insists on SSLv23 not SSLv3 + * Konq disables SSLv2 by default now, SSLv23 works + */ + + 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)); + return NULL; + } + this->ssl_ctx = SSL_CTX_new(method); /* create context */ + if (!this->ssl_ctx) { + fprintf(stderr, "problem creating ssl context: %s\n", + ERR_error_string(ERR_get_error(), ssl_err_buf)); + return NULL; + } + + /* client context */ + + method = (SSL_METHOD *)SSLv23_client_method(); + if (!method) { + fprintf(stderr, "problem creating ssl method: %s\n", + ERR_error_string(ERR_get_error(), ssl_err_buf)); + return NULL; + } + this->ssl_client_ctx = SSL_CTX_new(method); /* create context */ + if (!this->ssl_client_ctx) { + fprintf(stderr, "problem creating ssl context: %s\n", + ERR_error_string(ERR_get_error(), ssl_err_buf)); + return NULL; + } + + + /* openssl init for cert verification (used with client sockets) */ + + if (!SSL_CTX_load_verify_locations(this->ssl_client_ctx, NULL, + LWS_OPENSSL_CLIENT_CERTS)) { + fprintf(stderr, "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); + } + + if (this->use_ssl) { + + /* openssl init for server sockets */ - 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)); - return NULL; - } - ssl_ctx = SSL_CTX_new(method); /* create context */ - if (!ssl_ctx) { - printf("problem creating ssl context: %s\n", - ERR_error_string(ERR_get_error(), ssl_err_buf)); - return NULL; - } /* set the local certificate from CertFile */ - n = SSL_CTX_use_certificate_file(ssl_ctx, + n = SSL_CTX_use_certificate_file(this->ssl_ctx, ssl_cert_filepath, SSL_FILETYPE_PEM); if (n != 1) { fprintf(stderr, "problem getting cert '%s': %s\n", @@ -564,7 +717,7 @@ libwebsocket_create_context(int port, return NULL; } /* set the private key from KeyFile */ - if (SSL_CTX_use_PrivateKey_file(ssl_ctx, + if (SSL_CTX_use_PrivateKey_file(this->ssl_ctx, ssl_private_key_filepath, SSL_FILETYPE_PEM) != 1) { fprintf(stderr, "ssl problem getting key '%s': %s\n", @@ -573,7 +726,7 @@ libwebsocket_create_context(int port, return NULL; } /* verify private key */ - if (!SSL_CTX_check_private_key(ssl_ctx)) { + if (!SSL_CTX_check_private_key(this->ssl_ctx)) { fprintf(stderr, "Private SSL key doesn't match cert\n"); return NULL; } @@ -587,12 +740,6 @@ libwebsocket_create_context(int port, if (lws_b64_selftest()) return NULL; - - this = malloc(sizeof(struct libwebsocket_context)); - - this->protocols = protocols; - this->listen_port = port; - /* set up our external listening socket we serve on */ if (port) { @@ -641,9 +788,6 @@ libwebsocket_create_context(int port, this->fds[0].fd = sockfd; this->fds[0].events = POLLIN; this->count_protocols = 0; -#ifdef LWS_OPENSSL_SUPPORT - this->use_ssl = use_ssl; -#endif if (port) { listen(sockfd, 5); diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index 57ab481..a486221 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -26,9 +26,11 @@ enum libwebsocket_callback_reasons { LWS_CALLBACK_ESTABLISHED, + LWS_CALLBACK_CLIENT_ESTABLISHED, LWS_CALLBACK_CLOSED, LWS_CALLBACK_RECEIVE, LWS_CALLBACK_CLIENT_RECEIVE, + LWS_CALLBACK_CLIENT_WRITEABLE, LWS_CALLBACK_HTTP, LWS_CALLBACK_BROADCAST }; @@ -42,7 +44,9 @@ enum libwebsocket_write_protocol { LWS_WRITE_CLOSE, LWS_WRITE_PING, - LWS_WRITE_PONG + LWS_WRITE_PONG, + + LWS_WRITE_NO_FIN = 0x40 }; struct libwebsocket; @@ -67,15 +71,25 @@ struct libwebsocket_context; * You get an opportunity to initialize user data when called back with * LWS_CALLBACK_ESTABLISHED reason. * - * LWS_CALLBACK_ESTABLISHED: after successful websocket handshake + * LWS_CALLBACK_ESTABLISHED: after the server completes a handshake with + * an incoming client + * + * LWS_CALLBACK_CLIENT_ESTABLISHED: after your client connection completed + * a handshake with the remote server * * LWS_CALLBACK_CLOSED: when the websocket session ends * * 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 - * found at *in and is len bytes long + * + * LWS_CALLBACK_RECEIVE: data has appeared for this server endpoint from a + * remote client, it can be found at *in and is + * len bytes long + * + * LWS_CALLBACK_CLIENT_RECEIVE: data has appeared from the server for the + * client connection, it can be found at *in and + * is len bytes long * * LWS_CALLBACK_HTTP: an http request has come from a client that is not * asking to upgrade the connection to a websocket @@ -85,6 +99,13 @@ struct libwebsocket_context; * @in points to the URI path requested and * libwebsockets_serve_http_file() makes it very * simple to send back a file to the client. + * + * LWS_CALLBACK_CLIENT_WRITEABLE: if you call + * libwebsocket_callback_on_writable() on a connection, you will + * get this callback coming when the connection socket is able to + * accept another write packet without blocking. If it already + * was able to take another packet without blocking, you'll get + * this callback at the next call to the service loop function. */ extern int callback(struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, void *user, @@ -197,6 +218,17 @@ libwebsockets_broadcast(const struct libwebsocket_protocols *protocol, extern const struct libwebsocket_protocols * libwebsockets_get_protocol(struct libwebsocket *wsi); +extern int +libwebsocket_callback_on_writable(struct libwebsocket *wsi); + +extern int +libwebsocket_callback_on_writable_all_protocol( + const struct libwebsocket_protocols *protocol); + + +extern int +libwebsocket_rx_flow_control(struct libwebsocket *wsi, int enable); + extern size_t libwebsockets_remaining_packet_payload(struct libwebsocket *wsi); @@ -204,6 +236,7 @@ extern struct libwebsocket * libwebsocket_client_connect(struct libwebsocket_context *clients, const char *address, int port, + int ssl_connection, const char *path, const char *host, const char *origin, diff --git a/lib/parsers.c b/lib/parsers.c index 9d9efb3..8d56221 100644 --- a/lib/parsers.c +++ b/lib/parsers.c @@ -977,8 +977,6 @@ libwebsocket_04_frame_mask_generate(struct libwebsocket *wsi) * packet while not burdening the user code with any protocol knowledge. */ - /* FIXME FIN bit */ - int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, size_t len, enum libwebsocket_write_protocol protocol) { @@ -987,6 +985,11 @@ int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, int post = 0; unsigned int shift = 7; + if (len == 0) { + fprintf(stderr, "zero length libwebsocket_write attempt\n"); + return 0; + } + if (protocol == LWS_WRITE_HTTP) goto send_raw; @@ -999,7 +1002,7 @@ int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, /* chrome likes this as of 30 Oct */ /* Firefox 4.0b6 likes this as of 30 Oct */ case 0: - if (protocol == LWS_WRITE_BINARY) { + if ((protocol & 0xf) == LWS_WRITE_BINARY) { /* in binary mode we send 7-bit used length blocks */ pre = 1; while (len & (127 << shift)) { @@ -1031,7 +1034,7 @@ int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, case 4: - switch (protocol) { + switch (protocol & 0xf) { case LWS_WRITE_TEXT: n = LWS_WS_OPCODE_04__TEXT_FRAME; break; @@ -1054,12 +1057,8 @@ int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, return -1; } - /* - * We don't really support the metaframe concept with FIN. - * Just set FIN on every packet for now - */ - - n |= 1 << 7; + if (!(protocol & LWS_WRITE_NO_FIN)) + n |= 1 << 7; if (len < 126) { buf[-2] = n; @@ -1130,10 +1129,9 @@ int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, memcpy(&buf[0 - pre], wsi->frame_masking_nonce_04, 4); } - send_raw: #ifdef LWS_OPENSSL_SUPPORT - if (use_ssl) { + if (wsi->ssl) { n = SSL_write(wsi->ssl, buf - pre, len + pre + post); if (n < 0) { fprintf(stderr, "ERROR writing to socket\n"); @@ -1149,6 +1147,7 @@ send_raw: #ifdef LWS_OPENSSL_SUPPORT } #endif + debug("written %d bytes to client\n", (int)len); return 0; @@ -1199,6 +1198,8 @@ int libwebsockets_serve_http_file(struct libwebsocket *wsi, const char *file, n = 1; while (n > 0) { n = read(fd, buf, 512); + if (n <= 0) + continue; libwebsocket_write(wsi, (unsigned char *)buf, n, LWS_WRITE_HTTP); } diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index a263d83..dfeb373 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -67,11 +67,6 @@ static inline void debug(const char *format, ...) } #endif -#ifdef LWS_OPENSSL_SUPPORT -extern SSL_CTX *ssl_ctx; -extern int use_ssl; -#endif - #define MAX_CLIENTS 100 #define LWS_MAX_HEADER_NAME_LENGTH 64 @@ -79,7 +74,7 @@ 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 MAX_BROADCAST_PAYLOAD 2048 #define LWS_MAX_PROTOCOLS 10 #define MAX_WEBSOCKET_04_KEY_LEN 128 @@ -175,6 +170,8 @@ struct libwebsocket_context { int listen_port; #ifdef LWS_OPENSSL_SUPPORT int use_ssl; + SSL_CTX *ssl_ctx; + SSL_CTX *ssl_client_ctx; #endif struct libwebsocket_protocols *protocols; int count_protocols; @@ -224,6 +221,7 @@ struct libwebsocket { #ifdef LWS_OPENSSL_SUPPORT SSL *ssl; + BIO *client_bio; #endif void *user_space; diff --git a/lib/sha-1.c b/lib/sha-1.c index 1bec395..aea5d6a 100644 --- a/lib/sha-1.c +++ b/lib/sha-1.c @@ -146,27 +146,31 @@ sha1_step(struct sha1_ctxt *ctxt) for (t = 0; t < 20; t++) { s = t & 0x0f; - if (t >= 16) { - W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s)); - } + if (t >= 16) + W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ + W((s+2) & 0x0f) ^ W(s)); + tmp = S(5, a) + F0(b, c, d) + e + W(s) + K(t); e = d; d = c; c = S(30, b); b = a; a = tmp; } for (t = 20; t < 40; t++) { s = t & 0x0f; - W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s)); + W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ + W((s+2) & 0x0f) ^ W(s)); tmp = S(5, a) + F1(b, c, d) + e + W(s) + K(t); e = d; d = c; c = S(30, b); b = a; a = tmp; } for (t = 40; t < 60; t++) { s = t & 0x0f; - W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s)); + W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ + W((s+2) & 0x0f) ^ W(s)); tmp = S(5, a) + F2(b, c, d) + e + W(s) + K(t); e = d; d = c; c = S(30, b); b = a; a = tmp; } for (t = 60; t < 80; t++) { s = t & 0x0f; - W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s)); + W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ + W((s+2) & 0x0f) ^ W(s)); tmp = S(5, a) + F3(b, c, d) + e + W(s) + K(t); e = d; d = c; c = S(30, b); b = a; a = tmp; } diff --git a/libwebsockets-api-doc.html b/libwebsockets-api-doc.html index 0016684..f649d79 100644 --- a/libwebsockets-api-doc.html +++ b/libwebsockets-api-doc.html @@ -58,6 +58,45 @@ would call it with a timeout_ms of 0, so it returns immediately if nothing is pending, or as soon as it services whatever was pending.
+

libwebsocket_callback_on_writable - Request a callback when this socket becomes able to be written to without blocking

+int +libwebsocket_callback_on_writable +(struct libwebsocket * wsi) +

Arguments

+
+
wsi +
Websocket connection instance to get callback for +
+
+

libwebsocket_callback_on_writable_all_protocol - Request a callback for all connections using the given protocol when it becomes possible to write to each socket without blocking in turn.

+int +libwebsocket_callback_on_writable_all_protocol +(const struct libwebsocket_protocols * protocol) +

Arguments

+
+
protocol +
Protocol whose connections will get callbacks +
+
+

libwebsocket_rx_flow_control - Enable and disable socket servicing for receieved packets.

+int +libwebsocket_rx_flow_control +(struct libwebsocket * wsi, +int enable) +

Arguments

+
+
wsi +
Websocket connection instance to get callback for +
enable +
0 = disable read servicing for this connection, 1 = enable +
+

Description

+
+

+If the output side of a server process becomes choked, this allows flow +control for the input side. +

+

libwebsocket_create_context - Create the websocket handler

struct libwebsocket_context * libwebsocket_create_context @@ -258,6 +297,44 @@ when that is the case libwebsockets_remaining_packet_payload will return Many protocols won't care becuse their packets are always small.
+

libwebsocket_client_connect - Connect to another websocket server

+struct libwebsocket * +libwebsocket_client_connect +(struct libwebsocket_context * this, +const char * address, +int port, +int ssl_connection, +const char * path, +const char * host, +const char * origin, +const char * protocol) +

Arguments

+
+
this +
Websocket context +
address +
Remote server address, eg, "myserver.com" +
port +
Port to connect to on the remote server, eg, 80 +
ssl_connection +
0 = ws://, 1 = wss:// encrypted, 2 = wss:// allow self +signed certs +
path +
Websocket path on server +
host +
Hostname on server +
origin +
Socket origin name +
protocol +
Comma-separated list of protocols being asked for from +the server, or just one. The server will pick the one it +likes best. +
+

Description

+
+This function creates a connection to a remote server +
+

callback - User server actions

int callback @@ -293,7 +370,13 @@ LWS_CALLBACK_ESTABLISHED reason.

LWS_CALLBACK_ESTABLISHED

-after successful websocket handshake +after the server completes a handshake with +an incoming client +
+

LWS_CALLBACK_CLIENT_ESTABLISHED

+
+after your client connection completed +a handshake with the remote server

LWS_CALLBACK_CLOSED

@@ -307,8 +390,15 @@ special buffer requirements

LWS_CALLBACK_RECEIVE

-data has appeared for the server, it can be -found at *in and is len bytes long +data has appeared for this server endpoint from a +remote client, it can be found at *in and is +len bytes long +
+

LWS_CALLBACK_CLIENT_RECEIVE

+
+data has appeared from the server for the +client connection, it can be found at *in and +is len bytes long

LWS_CALLBACK_HTTP

@@ -321,6 +411,15 @@ which will then open the websockets connection. libwebsockets_serve_http_file makes it very simple to send back a file to the client.
+

LWS_CALLBACK_CLIENT_WRITEABLE

+
+if you call +libwebsocket_callback_on_writable on a connection, you will +get this callback coming when the connection socket is able to +accept another write packet without blocking. If it already +was able to take another packet without blocking, you'll get +this callback at the next call to the service loop function. +

struct libwebsocket_protocols - List of protocols and handlers server supports.

struct libwebsocket_protocols {
diff --git a/test-server/Makefile.am b/test-server/Makefile.am index 43d3c7c..c1083a5 100644 --- a/test-server/Makefile.am +++ b/test-server/Makefile.am @@ -3,6 +3,12 @@ libwebsockets_test_server_SOURCES=test-server.c libwebsockets_test_server_LDADD=-L../lib -lwebsockets libwebsockets_test_client_SOURCES=test-client.c libwebsockets_test_client_LDADD=-L../lib -lwebsockets + + + +libwebsockets_test_server_CFLAGS:= -Wall -Werror -std=gnu99 -pedantic -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\" +libwebsockets_test_client_CFLAGS:= -Wall -Werror -std=gnu99 -pedantic -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\" + # # cook a random test cert and key # notice your real cert and key will want to be 0600 permissions diff --git a/test-server/Makefile.in b/test-server/Makefile.in index 03ad8a8..c7555af 100644 --- a/test-server/Makefile.in +++ b/test-server/Makefile.in @@ -48,14 +48,24 @@ CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(bindir)" PROGRAMS = $(bin_PROGRAMS) -am_libwebsockets_test_client_OBJECTS = test-client.$(OBJEXT) +am_libwebsockets_test_client_OBJECTS = \ + libwebsockets_test_client-test-client.$(OBJEXT) libwebsockets_test_client_OBJECTS = \ $(am_libwebsockets_test_client_OBJECTS) libwebsockets_test_client_DEPENDENCIES = -am_libwebsockets_test_server_OBJECTS = test-server.$(OBJEXT) +libwebsockets_test_client_LINK = $(LIBTOOL) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(libwebsockets_test_client_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +am_libwebsockets_test_server_OBJECTS = \ + libwebsockets_test_server-test-server.$(OBJEXT) libwebsockets_test_server_OBJECTS = \ $(am_libwebsockets_test_server_OBJECTS) libwebsockets_test_server_DEPENDENCIES = +libwebsockets_test_server_LINK = $(LIBTOOL) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(libwebsockets_test_server_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles @@ -156,6 +166,7 @@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ +clientcertdir = @clientcertdir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ @@ -193,6 +204,8 @@ libwebsockets_test_server_SOURCES = test-server.c libwebsockets_test_server_LDADD = -L../lib -lwebsockets libwebsockets_test_client_SOURCES = test-client.c libwebsockets_test_client_LDADD = -L../lib -lwebsockets +libwebsockets_test_server_CFLAGS := -Wall -Werror -std=gnu99 -pedantic -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\" +libwebsockets_test_client_CFLAGS := -Wall -Werror -std=gnu99 -pedantic -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\" all: all-am .SUFFIXES: @@ -272,10 +285,10 @@ clean-binPROGRAMS: rm -f $$list libwebsockets-test-client$(EXEEXT): $(libwebsockets_test_client_OBJECTS) $(libwebsockets_test_client_DEPENDENCIES) @rm -f libwebsockets-test-client$(EXEEXT) - $(LINK) $(libwebsockets_test_client_OBJECTS) $(libwebsockets_test_client_LDADD) $(LIBS) + $(libwebsockets_test_client_LINK) $(libwebsockets_test_client_OBJECTS) $(libwebsockets_test_client_LDADD) $(LIBS) libwebsockets-test-server$(EXEEXT): $(libwebsockets_test_server_OBJECTS) $(libwebsockets_test_server_DEPENDENCIES) @rm -f libwebsockets-test-server$(EXEEXT) - $(LINK) $(libwebsockets_test_server_OBJECTS) $(libwebsockets_test_server_LDADD) $(LIBS) + $(libwebsockets_test_server_LINK) $(libwebsockets_test_server_OBJECTS) $(libwebsockets_test_server_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -283,8 +296,8 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-client.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-server.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_test_client-test-client.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_test_server-test-server.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @@ -307,6 +320,34 @@ distclean-compile: @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< +libwebsockets_test_client-test-client.o: test-client.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_client_CFLAGS) $(CFLAGS) -MT libwebsockets_test_client-test-client.o -MD -MP -MF $(DEPDIR)/libwebsockets_test_client-test-client.Tpo -c -o libwebsockets_test_client-test-client.o `test -f 'test-client.c' || echo '$(srcdir)/'`test-client.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libwebsockets_test_client-test-client.Tpo $(DEPDIR)/libwebsockets_test_client-test-client.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test-client.c' object='libwebsockets_test_client-test-client.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_client_CFLAGS) $(CFLAGS) -c -o libwebsockets_test_client-test-client.o `test -f 'test-client.c' || echo '$(srcdir)/'`test-client.c + +libwebsockets_test_client-test-client.obj: test-client.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_client_CFLAGS) $(CFLAGS) -MT libwebsockets_test_client-test-client.obj -MD -MP -MF $(DEPDIR)/libwebsockets_test_client-test-client.Tpo -c -o libwebsockets_test_client-test-client.obj `if test -f 'test-client.c'; then $(CYGPATH_W) 'test-client.c'; else $(CYGPATH_W) '$(srcdir)/test-client.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libwebsockets_test_client-test-client.Tpo $(DEPDIR)/libwebsockets_test_client-test-client.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test-client.c' object='libwebsockets_test_client-test-client.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_client_CFLAGS) $(CFLAGS) -c -o libwebsockets_test_client-test-client.obj `if test -f 'test-client.c'; then $(CYGPATH_W) 'test-client.c'; else $(CYGPATH_W) '$(srcdir)/test-client.c'; fi` + +libwebsockets_test_server-test-server.o: test-server.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_server_CFLAGS) $(CFLAGS) -MT libwebsockets_test_server-test-server.o -MD -MP -MF $(DEPDIR)/libwebsockets_test_server-test-server.Tpo -c -o libwebsockets_test_server-test-server.o `test -f 'test-server.c' || echo '$(srcdir)/'`test-server.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libwebsockets_test_server-test-server.Tpo $(DEPDIR)/libwebsockets_test_server-test-server.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test-server.c' object='libwebsockets_test_server-test-server.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_server_CFLAGS) $(CFLAGS) -c -o libwebsockets_test_server-test-server.o `test -f 'test-server.c' || echo '$(srcdir)/'`test-server.c + +libwebsockets_test_server-test-server.obj: test-server.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_server_CFLAGS) $(CFLAGS) -MT libwebsockets_test_server-test-server.obj -MD -MP -MF $(DEPDIR)/libwebsockets_test_server-test-server.Tpo -c -o libwebsockets_test_server-test-server.obj `if test -f 'test-server.c'; then $(CYGPATH_W) 'test-server.c'; else $(CYGPATH_W) '$(srcdir)/test-server.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libwebsockets_test_server-test-server.Tpo $(DEPDIR)/libwebsockets_test_server-test-server.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test-server.c' object='libwebsockets_test_server-test-server.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_server_CFLAGS) $(CFLAGS) -c -o libwebsockets_test_server-test-server.obj `if test -f 'test-server.c'; then $(CYGPATH_W) 'test-server.c'; else $(CYGPATH_W) '$(srcdir)/test-server.c'; fi` + mostlyclean-libtool: -rm -f *.lo @@ -514,6 +555,7 @@ uninstall-am: uninstall-binPROGRAMS mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags uninstall uninstall-am uninstall-binPROGRAMS + # # cook a random test cert and key # notice your real cert and key will want to be 0600 permissions diff --git a/test-server/test-client.c b/test-server/test-client.c index 3243ccd..3636951 100644 --- a/test-server/test-client.c +++ b/test-server/test-client.c @@ -35,11 +35,11 @@ * server just it simplifies the demo). * * dumb-increment-protocol: we connect to the server and print the number - * we are given + * we are given * * lws-mirror-protocol: draws random circles, which are mirrored on to every - * client (see them being drawn in every browser - * session also using the test server) + * client (see them being drawn in every browser + * session also using the test server) */ enum demo_protocols { @@ -62,7 +62,8 @@ callback_dumb_increment(struct libwebsocket *wsi, switch (reason) { case LWS_CALLBACK_CLIENT_RECEIVE: - fprintf(stderr, "rx %d '%s'\n", len, in); + ((char *)in)[len] = '\0'; + fprintf(stderr, "rx %d '%s'\n", (int)len, (char *)in); break; default: @@ -75,148 +76,49 @@ callback_dumb_increment(struct libwebsocket *wsi, /* lws-mirror_protocol */ -/* "how to draw a circle" */ - -struct coord { - int x; - int y; -}; - -static struct coord circle[] = { - -{ 0, 240 }, -{ 12, 239 }, -{ 25, 238 }, -{ 37, 237 }, -{ 49, 234 }, -{ 62, 231 }, -{ 74, 228 }, -{ 86, 224 }, -{ 97, 219 }, -{ 108, 213 }, -{ 120, 207 }, -{ 130, 201 }, -{ 141, 194 }, -{ 151, 186 }, -{ 160, 178 }, -{ 169, 169 }, -{ 178, 160 }, -{ 186, 151 }, -{ 194, 141 }, -{ 201, 130 }, -{ 207, 120 }, -{ 213, 108 }, -{ 219, 97 }, -{ 224, 86 }, -{ 228, 74 }, -{ 231, 62 }, -{ 234, 49 }, -{ 237, 37 }, -{ 238, 25 }, -{ 239, 12 }, -{ 240, 0 }, -{ 239, -12 }, -{ 238, -25 }, -{ 237, -37 }, -{ 234, -49 }, -{ 231, -62 }, -{ 228, -74 }, -{ 224, -86 }, -{ 219, -97 }, -{ 213, -108 }, -{ 207, -120 }, -{ 201, -130 }, -{ 194, -141 }, -{ 186, -151 }, -{ 178, -160 }, -{ 169, -169 }, -{ 160, -178 }, -{ 151, -186 }, -{ 141, -194 }, -{ 130, -201 }, -{ 120, -207 }, -{ 108, -213 }, -{ 97, -219 }, -{ 86, -224 }, -{ 74, -228 }, -{ 62, -231 }, -{ 49, -234 }, -{ 37, -237 }, -{ 25, -238 }, -{ 12, -239 }, -{ 0, -240 }, -{ -12, -239 }, -{ -25, -238 }, -{ -37, -237 }, -{ -49, -234 }, -{ -62, -231 }, -{ -74, -228 }, -{ -86, -224 }, -{ -97, -219 }, -{ -108, -213 }, -{ -119, -207 }, -{ -130, -201 }, -{ -141, -194 }, -{ -151, -186 }, -{ -160, -178 }, -{ -169, -169 }, -{ -178, -160 }, -{ -186, -151 }, -{ -194, -141 }, -{ -201, -130 }, -{ -207, -120 }, -{ -213, -108 }, -{ -219, -97 }, -{ -224, -86 }, -{ -228, -74 }, -{ -231, -62 }, -{ -234, -49 }, -{ -237, -37 }, -{ -238, -25 }, -{ -239, -12 }, -{ -240, 0 }, -{ -239, 12 }, -{ -238, 25 }, -{ -237, 37 }, -{ -234, 49 }, -{ -231, 62 }, -{ -228, 74 }, -{ -224, 86 }, -{ -219, 97 }, -{ -213, 108 }, -{ -207, 120 }, -{ -201, 130 }, -{ -194, 141 }, -{ -186, 151 }, -{ -178, 160 }, -{ -169, 169 }, -{ -160, 178 }, -{ -151, 186 }, -{ -141, 194 }, -{ -130, 201 }, -{ -119, 207 }, -{ -108, 213 }, -{ -97, 219 }, -{ -86, 224 }, -{ -74, 228 }, -{ -62, 231 }, -{ -49, 234 }, -{ -37, 237 }, -{ -25, 238 }, -{ -12, 239 }, -{ 0, 240 }, - -}; static int callback_lws_mirror(struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, void *user, void *in, size_t len) { + unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 4096 + + LWS_SEND_BUFFER_POST_PADDING]; + int l; + switch (reason) { + case LWS_CALLBACK_CLIENT_ESTABLISHED: + + /* + * start the ball rolling, + * LWS_CALLBACK_CLIENT_WRITEABLE will come immediately + */ + + libwebsocket_callback_on_writable(wsi); + break; + case LWS_CALLBACK_CLIENT_RECEIVE: -// fprintf(stderr, "rx %d '%s'\n", len, in); +/* fprintf(stderr, "rx %d '%s'\n", (int)len, (char *)in); */ + break; + + case LWS_CALLBACK_CLIENT_WRITEABLE: + + l = sprintf((char *)&buf[LWS_SEND_BUFFER_PRE_PADDING], + "c #%06X %d %d %d;", + (int)random() & 0xffffff, + (int)random() % 500, + (int)random() % 250, + (int)random() % 24); + + libwebsocket_write(wsi, + &buf[LWS_SEND_BUFFER_PRE_PADDING], l, LWS_WRITE_TEXT); + + /* get notified as soon as we can write again */ + + libwebsocket_callback_on_writable(wsi); + + usleep(200); break; default: @@ -245,9 +147,9 @@ static struct libwebsocket_protocols protocols[] = { }; static struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "port", required_argument, NULL, 'p' }, - { "ssl", no_argument, NULL, 's' }, + { "help", no_argument, NULL, 'h' }, + { "port", required_argument, NULL, 'p' }, + { "ssl", no_argument, NULL, 's' }, { NULL, 0, 0, 0 } }; @@ -258,19 +160,10 @@ int main(int argc, char **argv) int port = 7681; int use_ssl = 0; struct libwebsocket_context *context; - const char * address = argv[1]; + const char *address = argv[1]; struct libwebsocket *wsi_dumb; struct libwebsocket *wsi_mirror; - unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 1024 + - LWS_SEND_BUFFER_POST_PADDING]; - int len; - int i = 0; - int xofs; - int yofs; - int oldx; - int oldy; - int scale; - int colour; + fprintf(stderr, "libwebsockets test client\n" "(C) Copyright 2010 Andy Green " @@ -287,7 +180,7 @@ int main(int argc, char **argv) continue; switch (n) { case 's': - use_ssl = 1; + use_ssl = 2; /* 2 = allow selfsigned */ break; case 'p': port = atoi(optarg); @@ -315,8 +208,8 @@ int main(int argc, char **argv) /* create a client websocket using dumb increment protocol */ - wsi_dumb = libwebsocket_client_connect(context, address, port, "/", - "http://host", "origin", + wsi_dumb = libwebsocket_client_connect(context, address, port, use_ssl, + "/", "http://host", "origin", protocols[PROTOCOL_DUMB_INCREMENT].name); if (wsi_dumb == NULL) { @@ -326,8 +219,8 @@ int main(int argc, char **argv) /* create a client websocket using mirror protocol */ - wsi_mirror = libwebsocket_client_connect(context, address, port, "/", - "http://host", "origin", + wsi_mirror = libwebsocket_client_connect(context, address, port, + use_ssl, "/", "http://host", "origin", protocols[PROTOCOL_LWS_MIRROR].name); if (wsi_mirror == NULL) { @@ -343,39 +236,8 @@ int main(int argc, char **argv) */ n = 0; - while (n >= 0) { - - usleep(10000); - - if (i == sizeof circle / sizeof circle[0]) - i = 0; - - if (i == 0) { - xofs = random() % 500; - yofs = random() % 250; - scale = random() % 24; - if (!scale) - scale = 1; - - oldx = xofs + (circle[i].x / scale); - oldy = yofs + (circle[i].y / scale); - colour = random() & 0xffffff; - } - - len = sprintf(&buf[LWS_SEND_BUFFER_PRE_PADDING], - "d #%06X %d %d %d %d", colour, oldx, oldy, - xofs + (circle[i].x / scale), - yofs + (circle[i].y / scale)); - oldx = xofs + (circle[i].x / scale); - oldy = yofs + (circle[i].y / scale); - i++; - - libwebsocket_write(wsi_mirror, - &buf[LWS_SEND_BUFFER_PRE_PADDING], len, LWS_WRITE_TEXT); - - - n = libwebsocket_service(context, 0); - } + while (n >= 0) + n = libwebsocket_service(context, 1000); libwebsocket_client_close(wsi_dumb); libwebsocket_client_close(wsi_mirror); diff --git a/test-server/test-server.c b/test-server/test-server.c index 832a45a..4f4512f 100644 --- a/test-server/test-server.c +++ b/test-server/test-server.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "../lib/libwebsockets.h" @@ -36,11 +37,11 @@ * * dumb-increment-protocol: once the socket is opened, an incrementing * ascii string is sent down it every 50ms. - * If you send "reset\n" on the websocket, then - * the incrementing number is reset to 0. + * If you send "reset\n" on the websocket, then + * the incrementing number is reset to 0. * * lws-mirror-protocol: copies any received packet to every connection also - * using this protocol, including the sender + * using this protocol, including the sender */ enum demo_protocols { @@ -55,7 +56,7 @@ enum demo_protocols { }; -#define LOCAL_RESOURCE_PATH "/usr/share/libwebsockets-test-server" +#define LOCAL_RESOURCE_PATH DATADIR"/libwebsockets-test-server" /* this protocol server (always the first one) just knows how to do HTTP */ @@ -65,7 +66,7 @@ static int callback_http(struct libwebsocket *wsi, { switch (reason) { case LWS_CALLBACK_HTTP: - fprintf(stderr, "serving HTTP URI %s\n", in); + fprintf(stderr, "serving HTTP URI %s\n", (char *)in); if (in && strcmp(in, "/favicon.ico") == 0) { if (libwebsockets_serve_http_file(wsi, @@ -108,9 +109,9 @@ callback_dumb_increment(struct libwebsocket *wsi, void *user, void *in, size_t len) { int n; - char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 + + unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 + LWS_SEND_BUFFER_POST_PADDING]; - char *p = (char *)&buf[LWS_SEND_BUFFER_PRE_PADDING]; + unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING]; struct per_session_data__dumb_increment *pss = user; switch (reason) { @@ -126,7 +127,7 @@ callback_dumb_increment(struct libwebsocket *wsi, */ case LWS_CALLBACK_BROADCAST: - n = sprintf(p, "%d", pss->number++); + n = sprintf((char *)p, "%d", pss->number++); n = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT); if (n < 0) { fprintf(stderr, "ERROR writing to socket"); @@ -135,7 +136,7 @@ callback_dumb_increment(struct libwebsocket *wsi, break; case LWS_CALLBACK_RECEIVE: - fprintf(stderr, "rx %d\n", len); + fprintf(stderr, "rx %d\n", (int)len); if (len < 6) break; if (strcmp(in, "reset\n") == 0) @@ -152,33 +153,92 @@ callback_dumb_increment(struct libwebsocket *wsi, /* lws-mirror_protocol */ +#define MAX_MESSAGE_QUEUE 64 + +struct per_session_data__lws_mirror { + struct libwebsocket *wsi; + int ringbuffer_tail; +}; + +struct a_message { + void *payload; + size_t len; +}; + +static struct a_message ringbuffer[MAX_MESSAGE_QUEUE]; +static int ringbuffer_head; + + static int callback_lws_mirror(struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, void *user, void *in, size_t len) { int n; + struct per_session_data__lws_mirror *pss = user; switch (reason) { + case LWS_CALLBACK_ESTABLISHED: + pss->ringbuffer_tail = ringbuffer_head; + pss->wsi = wsi; + break; + + case LWS_CALLBACK_CLIENT_WRITEABLE: + if (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++; + + if (((ringbuffer_head - pss->ringbuffer_tail) % + MAX_MESSAGE_QUEUE) < (MAX_MESSAGE_QUEUE - 15)) + libwebsocket_rx_flow_control(wsi, 1); + + libwebsocket_callback_on_writable(wsi); + + } + break; + case LWS_CALLBACK_BROADCAST: n = libwebsocket_write(wsi, in, len, LWS_WRITE_TEXT); + if (n < 0) + fprintf(stderr, "mirror write failed\n"); break; case LWS_CALLBACK_RECEIVE: - /* - * 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); + + 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((char *)ringbuffer[ringbuffer_head].payload + + LWS_SEND_BUFFER_PRE_PADDING, in, len); + if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1)) + ringbuffer_head = 0; + else + ringbuffer_head++; + + if (((ringbuffer_head - pss->ringbuffer_tail) % + MAX_MESSAGE_QUEUE) > (MAX_MESSAGE_QUEUE - 10)) + libwebsocket_rx_flow_control(wsi, 0); + + libwebsocket_callback_on_writable_all_protocol( + libwebsockets_get_protocol(wsi)); break; default: @@ -206,6 +266,8 @@ static struct libwebsocket_protocols protocols[] = { [PROTOCOL_LWS_MIRROR] = { .name = "lws-mirror-protocol", .callback = callback_lws_mirror, + .per_session_data_size = + sizeof(struct per_session_data__lws_mirror), }, [DEMO_PROTOCOL_COUNT] = { /* end of list */ .callback = NULL @@ -213,9 +275,9 @@ static struct libwebsocket_protocols protocols[] = { }; static struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "port", required_argument, NULL, 'p' }, - { "ssl", no_argument, NULL, 's' }, + { "help", no_argument, NULL, 'h' }, + { "port", required_argument, NULL, 'p' }, + { "ssl", no_argument, NULL, 's' }, { NULL, 0, 0, 0 } }; @@ -231,6 +293,9 @@ int main(int argc, char **argv) int port = 7681; int use_ssl = 0; struct libwebsocket_context *context; +#ifdef LWS_NO_FORK + unsigned int oldus = 0; +#endif fprintf(stderr, "libwebsockets test server\n" "(C) Copyright 2010-2011 Andy Green " @@ -275,13 +340,14 @@ int main(int argc, char **argv) fprintf(stderr, " Using no-fork service loop\n"); while (1) { - - usleep(50000); + struct timeval tv; + + gettimeofday(&tv, NULL); /* * This broadcasts to all dumb-increment-protocol connections * at 20Hz. - * + * * We're just sending a character 'x', in these examples the * callbacks send their own per-connection content. * @@ -291,8 +357,12 @@ int main(int argc, char **argv) * We take care of pre-and-post padding allocation. */ - libwebsockets_broadcast(&protocols[PROTOCOL_DUMB_INCREMENT], + if (((unsigned int)tv.tv_usec - oldus) > 50000) { + libwebsockets_broadcast( + &protocols[PROTOCOL_DUMB_INCREMENT], &buf[LWS_SEND_BUFFER_PRE_PADDING], 1); + oldus = tv.tv_usec; + } /* * This example server does not fork or create a thread for @@ -304,7 +374,7 @@ int main(int argc, char **argv) * immediately and quickly. */ - libwebsocket_service(context, 0); + libwebsocket_service(context, 50); } #else @@ -327,13 +397,13 @@ int main(int argc, char **argv) } while (1) { - + usleep(50000); /* * This broadcasts to all dumb-increment-protocol connections * at 20Hz. - * + * * We're just sending a character 'x', in these examples the * callbacks send their own per-connection content. * diff --git a/test-server/test.html b/test-server/test.html index 466310a..5f030f3 100644 --- a/test-server/test.html +++ b/test-server/test.html @@ -7,7 +7,8 @@

libwebsockets "dumb-increment-protocol" test applet

-The incrementing number is coming from the server. +The incrementing number is coming from the server and is individual for +each connection to the server... try opening a second browser window. Click the button to send the server a websocket message to reset the number.

@@ -27,6 +28,8 @@ well. The lws-mirror protocol doesn't interpret what is being sent to it, it just re-sends it to every other websocket it has a connection with using that protocol, including the guy who sent the packet. +

libwebsockets-test-client spams circles on to this shared canvas when +run.



@@ -121,13 +124,25 @@ function reset() { } socket_lm.onmessage =function got_packet(msg) { - i = msg.data.split(' '); - if (i[0] == 'd') { - ctx.strokeStyle = i[1]; - ctx.beginPath(); - ctx.moveTo(+(i[2]), +(i[3])); - ctx.lineTo(+(i[4]), +(i[5])); - ctx.stroke(); + j = msg.data.split(';'); + f = 0; + while (f < j.length - 1) { + i = j[f].split(' '); + if (i[0] == 'd') { + ctx.strokeStyle = i[1]; + ctx.beginPath(); + ctx.moveTo(+(i[2]), +(i[3])); + ctx.lineTo(+(i[4]), +(i[5])); + ctx.stroke(); + } + if (i[0] == 'c') { + ctx.strokeStyle = i[1]; + ctx.beginPath(); + ctx.arc(+(i[2]), +(i[3]), +(i[4]), 0, Math.PI*2, true); + ctx.stroke(); + } + + f++; } } @@ -192,7 +207,7 @@ function ev_mousemove (ev) { last_y = y; return; } - socket_lm.send("d " + color + " " + last_x + " " + last_y + " " + x + ' ' + y); + socket_lm.send("d " + color + " " + last_x + " " + last_y + " " + x + ' ' + y + ';'); last_x = x; last_y = y; -- 2.7.4