diff options
author | Andy Green <andy@warmcat.com> | 2010-11-12 10:44:16 +0000 |
---|---|---|
committer | Andy Green <andy@warmcat.com> | 2010-11-12 10:44:16 +0000 |
commit | 4f3943a8f818be392ac9f059589f341ff311affa (patch) | |
tree | 1da5d0d3a7b519d89b9d1ec7b464d7c917fcb903 /lib | |
parent | ab7d933d9d7aa961a3ae398adf42d401334e066e (diff) |
move-to-automatic-protocol-list-scheme.patch
Signed-off-by: Andy Green <andy@warmcat.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile.am | 4 | ||||
-rw-r--r-- | lib/Makefile.in | 4 | ||||
-rw-r--r-- | lib/handshake.c | 52 | ||||
-rw-r--r-- | lib/libwebsockets.c | 119 | ||||
-rw-r--r-- | lib/libwebsockets.h | 37 | ||||
-rw-r--r-- | lib/parsers.c | 6 | ||||
-rw-r--r-- | lib/private-libwebsockets.h | 6 |
7 files changed, 158 insertions, 70 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index 78ced38..7cc2120 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -12,7 +12,7 @@ libwebsockets_la_LDFLAGS=-version-info 0:1 all-local: ../scripts/kernel-doc -html \ libwebsockets.c \ - parsers.c \ - ../test-server/test-server.c \ + parsers.c \ + libwebsockets.h \ > ../libwebsockets-api-doc.html diff --git a/lib/Makefile.in b/lib/Makefile.in index 493db5b..dcf4a67 100644 --- a/lib/Makefile.in +++ b/lib/Makefile.in @@ -578,8 +578,8 @@ uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES all-local: ../scripts/kernel-doc -html \ libwebsockets.c \ - parsers.c \ - ../test-server/test-server.c \ + parsers.c \ + libwebsockets.h \ > ../libwebsockets-api-doc.html # Tell versions [3.59,3.63) of GNU make to not export all variables. diff --git a/lib/handshake.c b/lib/handshake.c index 2ca7938..7deb581 100644 --- a/lib/handshake.c +++ b/lib/handshake.c @@ -94,9 +94,9 @@ libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len) if (!wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len || !wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len) { - if (wsi->callback) - (wsi->callback)(wsi, LWS_CALLBACK_HTTP, - &wsi->user_space[0], + if (wsi->protocol->callback) + (wsi->protocol->callback)(wsi, LWS_CALLBACK_HTTP, + &wsi->user_space, wsi->utf8_token[WSI_TOKEN_GET_URI].token, 0); wsi->state = WSI_STATE_HTTP; return 0; @@ -132,11 +132,41 @@ libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len) /* Make sure user side is happy about protocol */ - if (wsi->callback) - wsi->callback(wsi, LWS_CALLBACK_PROTOCOL_FILTER, - &wsi->user_space[0], - wsi->utf8_token[WSI_TOKEN_PROTOCOL].token, - 0); + while (wsi->protocol->callback) { + + if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token == NULL) { + if (wsi->protocol->name == NULL) + break; + } else + if (strcmp( + wsi->utf8_token[WSI_TOKEN_PROTOCOL].token, + wsi->protocol->name) == 0) + break; + + wsi->protocol++; + } + if (wsi->protocol->callback == NULL) { + if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token == NULL) + fprintf(stderr, "[no protocol] " + "not supported (use NULL .name)\n"); + else + fprintf(stderr, "Requested protocol %s " + "not supported\n", + wsi->utf8_token[WSI_TOKEN_PROTOCOL].token); + goto bail; + } + + /* allocate the per-connection user memory (if any) */ + + if (wsi->protocol->per_session_data_size) { + wsi->user_space = malloc( + wsi->protocol->per_session_data_size); + if (wsi->user_space == NULL) { + fprintf(stderr, "Out of memory for " + "conn user space\n"); + goto bail; + } + } /* create the response packet */ @@ -243,9 +273,9 @@ libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len) /* notify user code that we're ready to roll */ - if (wsi->callback) - wsi->callback(wsi, LWS_CALLBACK_ESTABLISHED, - &wsi->user_space[0], NULL, 0); + if (wsi->protocol->callback) + wsi->protocol->callback(wsi, LWS_CALLBACK_ESTABLISHED, + &wsi->user_space, NULL, 0); break; case WSI_STATE_ESTABLISHED: diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index 11ab790..60ecde2 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -31,6 +31,48 @@ extern int libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len); +/* document the generic callback (it's a fake prototype under this) */ +/** + * callback() - User server actions + * @wsi: Opaque websocket instance pointer + * @reason: The reason for the call + * @user: Pointer to per-session user data allocated by library + * @in: Pointer used for some callback reasons + * @len: Length set for some callback reasons + * + * This callback is the way the user controls what is served. All the + * protocol detail is hidden and handled by the library. + * + * For each connection / session there is user data allocated that is + * pointed to by "user". You set the size of this user data area when + * the library is initialized with libwebsocket_create_server. + * + * 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_CLOSED: when the websocket session ends + * + * LWS_CALLBACK_SEND: opportunity 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_HTTP: an http request has come from a client that is not + * asking to upgrade the connection to a websocket + * one. This is a chance to serve http content, + * for example, to send a script to the client + * which will then open the websockets connection. + * @in points to the URI path requested and + * libwebsockets_serve_http_file() makes it very + * simple to send back a file to the client. + */ +extern int callback(struct libwebsocket * wsi, + enum libwebsocket_callback_reasons reason, void * user, + void *in, size_t len); + void libwebsocket_close_and_free_session(struct libwebsocket *wsi) @@ -39,8 +81,8 @@ libwebsocket_close_and_free_session(struct libwebsocket *wsi) wsi->state = WSI_STATE_DEAD_SOCKET; - if (wsi->callback && n == WSI_STATE_ESTABLISHED) - wsi->callback(wsi, LWS_CALLBACK_CLOSED, &wsi->user_space[0], + if (wsi->protocol->callback && n == WSI_STATE_ESTABLISHED) + wsi->protocol->callback(wsi, LWS_CALLBACK_CLOSED, &wsi->user_space, NULL, 0); for (n = 0; n < WSI_TOKEN_COUNT; n++) @@ -62,17 +104,18 @@ libwebsocket_close_and_free_session(struct libwebsocket *wsi) #ifdef LWS_OPENSSL_SUPPORT } #endif + if (wsi->user_space) + free(wsi->user_space); + free(wsi); } /** * libwebsocket_create_server() - Create the listening websockets server * @port: Port to listen on - * @callback: The callback in user code to perform actual serving - * @user_area_size: How much memory to allocate per connection session - * which will be used by the user application to store - * per-session data. A pointer to this space is given - * when the user callback is called. + * @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. * @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 @@ -98,10 +141,7 @@ libwebsocket_close_and_free_session(struct libwebsocket *wsi) */ int libwebsocket_create_server(int port, - int (*callback)(struct libwebsocket *, - enum libwebsocket_callback_reasons, - void *, void *, size_t), - size_t user_area_size, + const struct libwebsocket_protocols *protocols, const char * ssl_cert_filepath, const char * ssl_private_key_filepath, int gid, int uid) @@ -185,14 +225,7 @@ int libwebsocket_create_server(int port, /* SSL is happy and has a cert it's content with */ } #endif - - if (!callback) { - fprintf(stderr, "callback is not optional!\n"); - return -1; - } - - /* sit there listening for connects, accept and spawn session servers */ - + sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { fprintf(stderr, "ERROR opening socket"); @@ -226,7 +259,7 @@ int libwebsocket_create_server(int port, if (n) return sockfd; - // drop any root privs for this thread + /* drop any root privs for this thread */ if (gid != -1) if (setgid(gid)) @@ -235,8 +268,11 @@ int libwebsocket_create_server(int port, if (setuid(uid)) fprintf(stderr, "setuid: %s\n", strerror(errno)); - /* we are running in a forked subprocess now */ - + /* + * sit there listening for connects, accept and service connections + * in a poll loop, without any further forking + */ + listen(sockfd, 5); fprintf(stderr, " Listening on port %d\n", port); @@ -273,8 +309,7 @@ int libwebsocket_create_server(int port, continue; } - wsi[fds_count] = malloc(sizeof(struct libwebsocket) + - user_area_size); + wsi[fds_count] = malloc(sizeof(struct libwebsocket)); if (!wsi[fds_count]) return -1; @@ -313,11 +348,10 @@ int libwebsocket_create_server(int port, ntohs(cli_addr.sin_port), fd, SSL_get_version(wsi[fds_count]->ssl)); - } else { -// fprintf(stderr, "accepted new conn port %u on fd=%d\n", -// ntohs(cli_addr.sin_port), fd); - } + } else #endif + debug("accepted new conn port %u on fd=%d\n", + ntohs(cli_addr.sin_port), fd); /* intialize the instance struct */ @@ -330,11 +364,20 @@ int libwebsocket_create_server(int port, wsi[fds_count]->utf8_token[n].token_len = 0; } - wsi[fds_count]->callback = callback; + /* + * these can only be set once the protocol is known + * we set an unestablished connection's protocol pointer + * 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; + /* * Default protocol is 76 * After 76, there's a header specified to inform which - * draft the client wants + * draft the client wants, when that's seen we modify + * the individual connection's spec revision accordingly */ wsi[fds_count]->ietf_spec_revision = 76; @@ -361,8 +404,6 @@ int libwebsocket_create_server(int port, if (!(fds[client].revents & POLLIN)) continue; - -// fprintf(stderr, "POLLIN\n"); #ifdef LWS_OPENSSL_SUPPORT if (use_ssl) @@ -371,8 +412,6 @@ int libwebsocket_create_server(int port, #endif n = recv(fds[client].fd, buf, sizeof(buf), 0); -// fprintf(stderr, "read returned %d\n", n); - if (n < 0) { fprintf(stderr, "Socket read returned %d\n", n); continue; @@ -389,7 +428,10 @@ int libwebsocket_create_server(int port, if (libwebsocket_read(wsi[client], buf, n) >= 0) continue; - /* it closed and nuked wsi[client] */ + /* + * it closed and nuked wsi[client], so remove the + * socket handle and wsi from our service list + */ nuke_this: for (n = client; n < fds_count - 1; n++) { fds[n] = fds[n + 1]; @@ -404,12 +446,9 @@ poll_out: if (wsi[client]->state != WSI_STATE_ESTABLISHED) continue; - - if (!wsi[client]->callback) - continue; - wsi[client]->callback(wsi[client], LWS_CALLBACK_SEND, - &wsi[client]->user_space[0], NULL, 0); + wsi[client]->protocol->callback(wsi[client], LWS_CALLBACK_SEND, + &wsi[client]->user_space, NULL, 0); } continue; diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index b1588b1..24042fc 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -29,7 +29,6 @@ enum libwebsocket_callback_reasons { LWS_CALLBACK_SEND, LWS_CALLBACK_RECEIVE, LWS_CALLBACK_HTTP, - LWS_CALLBACK_PROTOCOL_FILTER, }; enum libwebsocket_write_protocol { @@ -40,14 +39,36 @@ enum libwebsocket_write_protocol { struct libwebsocket; +/** + * struct libwebsocket_protocols - List of protocols and handlers server + * supports. + * @name: Protocol name that must match the one given in the client + * Javascript new WebSocket(url, 'protocol') name + * @callback: The service callback used for this protocol. It allows the + * service action for an entire protocol to be encapsulated in + * the protocol-specific callback + * @per_session_data_size: Each new connection using this protocol gets + * 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 + * + * This structure represents one protocol supported by the server. An + * array of these structures is passed to libwebsocket_create_server() + * allows as many protocols as you like to be handled by one server. + */ + +struct libwebsocket_protocols { + const char *name; + int (*callback)(struct libwebsocket * wsi, + enum libwebsocket_callback_reasons reason, void * user, + void *in, size_t len); + size_t per_session_data_size; +}; + extern int libwebsocket_create_server(int port, - int (*callback)(struct libwebsocket *wsi, - enum libwebsocket_callback_reasons reason, - void *user, void *in, size_t len), - size_t user_space, - const char * ssl_cert_filepath, - const char * ssl_private_key_filepath, - int gid, int uid); + const struct libwebsocket_protocols *protocols, + const char * ssl_cert_filepath, + const char * ssl_private_key_filepath, int gid, int uid); /* * IMPORTANT NOTICE! diff --git a/lib/parsers.c b/lib/parsers.c index 9a767f8..52cd126 100644 --- a/lib/parsers.c +++ b/lib/parsers.c @@ -210,9 +210,9 @@ static int libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c) if (wsi->rx_user_buffer_head != MAX_USER_RX_BUFFER) break; issue: - if (wsi->callback) - wsi->callback(wsi, LWS_CALLBACK_RECEIVE, - &wsi->user_space[0], + if (wsi->protocol->callback) + wsi->protocol->callback(wsi, LWS_CALLBACK_RECEIVE, + &wsi->user_space, &wsi->rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING], wsi->rx_user_buffer_head); wsi->rx_user_buffer_head = 0; diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 27f495a..d3789f1 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -121,8 +121,7 @@ struct lws_tokens { */ struct libwebsocket { - int (*callback)(struct libwebsocket *, - enum libwebsocket_callback_reasons reason, void *, void *, size_t); + const struct libwebsocket_protocols *protocol; enum lws_connection_states state; @@ -145,8 +144,7 @@ struct libwebsocket { SSL *ssl; #endif - /* last */ - char user_space[0]; + void *user_space; }; extern void |