From: Andy Green Date: Sun, 23 Jan 2011 16:50:33 +0000 (+0000) Subject: clean--fix-sigpipe.patch X-Git-Tag: upstream/1.7.3~1450 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=6964bb5ee5c3ce044cb870541cd6f123bc313733;p=platform%2Fupstream%2Flibwebsockets.git clean--fix-sigpipe.patch Signed-off-by: Andy Green --- diff --git a/lib/client-handshake.c b/lib/client-handshake.c index 1ef80f4..64f72cb 100644 --- a/lib/client-handshake.c +++ b/lib/client-handshake.c @@ -89,13 +89,13 @@ libwebsocket_client_connect(struct libwebsocket_context *clients, "61AC5F19-FBBA-4540-B96F-6561F1AB40A8"; char pkt[1024]; char *p = &pkt[0]; - const char * pc; + const char *pc; int len; int okay = 0; struct libwebsocket *wsi; int n; - wsi = malloc(sizeof (struct libwebsocket)); + wsi = malloc(sizeof(struct libwebsocket)); if (wsi == NULL) return NULL; @@ -123,7 +123,7 @@ libwebsocket_client_connect(struct libwebsocket_context *clients, } wsi->sock = socket(AF_INET, SOCK_STREAM, 0); - + if (wsi->sock < 0) { fprintf(stderr, "Unable to open socket\n"); goto bail1; @@ -139,11 +139,11 @@ libwebsocket_client_connect(struct libwebsocket_context *clients, sizeof(struct sockaddr)) == -1) { fprintf(stderr, "Connect failed"); goto bail1; - } + } - /* - * create the random key - */ + /* + * create the random key + */ fd = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY); if (fd < 1) { @@ -160,7 +160,7 @@ libwebsocket_client_connect(struct libwebsocket_context *clients, } close(fd); - lws_b64_encode_string(hash, 16, key_b64, sizeof key_b64); + lws_b64_encode_string(hash, 16, key_b64, sizeof key_b64); /* * 04 example client handshake @@ -283,7 +283,7 @@ libwebsocket_client_connect(struct libwebsocket_context *clients, " Upgrade header '%s'\n", wsi->utf8_token[WSI_TOKEN_UPGRADE].token); goto bail2; - } + } strtolower(wsi->utf8_token[WSI_TOKEN_CONNECTION].token); if (strcmp(wsi->utf8_token[WSI_TOKEN_CONNECTION].token, "upgrade")) { diff --git a/lib/handshake.c b/lib/handshake.c index 842b764..d6928d7 100644 --- a/lib/handshake.c +++ b/lib/handshake.c @@ -376,7 +376,7 @@ handshake_04(struct libwebsocket *wsi) m += nonce_len; strcpy(m, websocket_magic_guid_04_masking); m += strlen(websocket_magic_guid_04_masking); - + SHA1((unsigned char *)mask_summing_buf, m - mask_summing_buf, wsi->masking_key_04); @@ -564,9 +564,9 @@ libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len) return 0; } - if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0) goto bail; + break; default: break; @@ -576,5 +576,6 @@ libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len) bail: libwebsocket_close_and_free_session(wsi); + return -1; } diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index aae6690..1c4bd17 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -86,7 +86,8 @@ libwebsocket_poll_connections(struct libwebsocket_context *this) if (this->fds[client].revents & (POLLERR | POLLHUP)) { debug("Session Socket %d %p (fd=%d) dead\n", - client, this->wsi[client], this->fds[client]); + client, (void *)this->wsi[client], + this->fds[client].fd); libwebsocket_close_and_free_session(this->wsi[client]); goto nuke_this; @@ -132,7 +133,7 @@ libwebsocket_poll_connections(struct libwebsocket_context *this) continue; /* only to clients connected to us */ - + if (wsi->client_mode) continue; @@ -174,9 +175,9 @@ libwebsocket_poll_connections(struct libwebsocket_context *this) /* service incoming data */ - if (libwebsocket_read(this->wsi[client], buf, n) >= 0) + n = libwebsocket_read(this->wsi[client], buf, n); + if (n >= 0) continue; - /* * it closed and nuked wsi[client], so remove the * socket handle and wsi from our service list @@ -184,19 +185,82 @@ libwebsocket_poll_connections(struct libwebsocket_context *this) nuke_this: debug("nuking wsi %p, fsd_count = %d\n", - this->wsi[client], this->fds_count - 1); + (void *)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; + } return 0; } +/** + * libwebsocket_context_destroy() - Destroy the websocket context + * @this: Websocket context + * + * This function closes any active connections and then frees the + * context. After calling this, any further use of the context is + * undefined. + */ +void +libwebsocket_context_destroy(struct libwebsocket_context *this) +{ + int client; + + /* close listening skt and per-protocol broadcast sockets */ + for (client = 0; client < this->fds_count; client++) + libwebsocket_close_and_free_session(this->wsi[client]); + +#ifdef LWS_OPENSSL_SUPPORT + if (ssl_ctx) + SSL_CTX_free(ssl_ctx); +#endif + + if (this) + free(this); +} + +/** + * libwebsocket_service() - Service any pending websocket activity + * @this: Websocket context + * @timeout_ms: Timeout for poll; 0 means return immediately if nothing needed + * service otherwise block and service immediately, returning + * after the timeout if nothing needed service. + * + * This function deals with any pending websocket traffic, for three + * kinds of event. It handles these events on both server and client + * types of connection the same. + * + * 1) Accept new connections to our context's server + * + * 2) Perform pending broadcast writes initiated from other forked + * processes (effectively serializing asynchronous broadcasts) + * + * 3) Call the receive callback for incoming frame data received by + * server or client connections. + * + * You need to call this service function periodically to all the above + * functions to happen; if your application is single-threaded you can + * just call it in your main event loop. + * + * Alternatively you can fork a new process that asynchronously handles + * calling this service in a loop. In that case you are happy if this + * call blocks your thread until it needs to take care of something and + * would call it with a large nonzero timeout. Your loop then takes no + * CPU while there is nothing happening. + * + * If you are calling it in a single-threaded app, you don't want it to + * wait around blocking other things in your loop from happening, so you + * 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. + */ + int libwebsocket_service(struct libwebsocket_context *this, int timeout_ms) @@ -218,7 +282,7 @@ libwebsocket_service(struct libwebsocket_context *this, int timeout_ms) n = poll(this->fds, this->fds_count, timeout_ms); else n = poll(&this->fds[1], this->fds_count - 1, timeout_ms); - + if (n < 0 || this->fds[0].revents & (POLLERR | POLLHUP)) { fprintf(stderr, "Listen Socket dead\n"); @@ -369,6 +433,8 @@ fill_in_fds: fatal: + fprintf(stderr, "service hits fatal\n"); + /* close listening skt and per-protocol broadcast sockets */ for (client = 0; client < this->fds_count; client++) close(this->fds[0].fd); @@ -376,7 +442,6 @@ fatal: #ifdef LWS_OPENSSL_SUPPORT SSL_CTX_free(ssl_ctx); #endif - kill(0, SIGTERM); if (this) free(this); @@ -392,12 +457,12 @@ fatal: /** * libwebsocket_create_context() - Create the websocket handler * @port: Port to listen on... you can use 0 to suppress listening on - * any port, that's what you want if you are not running a - * websocket server at all but just using it as a client + * any port, that's what you want if you are not running a + * websocket server at all but just using it as a client * @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 + * 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 @@ -644,10 +709,10 @@ libwebsocket_create_context(int port, /** * libwebsockets_fork_service_loop() - Optional helper function forks off * a process for the websocket server loop. - * You don't have to use this but if not, you - * have to make sure you are calling - * libwebsocket_service periodically to service - * the websocket traffic + * You don't have to use this but if not, you + * have to make sure you are calling + * libwebsocket_service periodically to service + * the websocket traffic * @this: server context returned by creation function */ @@ -798,7 +863,7 @@ libwebsockets_broadcast(const struct libwebsocket_protocols *protocol, * set up when the websocket server initializes */ - n = send(protocol->broadcast_socket_user_fd, buf, len, 0); + n = send(protocol->broadcast_socket_user_fd, buf, len, MSG_NOSIGNAL); return n; } diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index b6d7335..57ab481 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -59,11 +59,11 @@ struct libwebsocket_context; * * 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. * @@ -77,45 +77,45 @@ struct libwebsocket_context; * 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 + * 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 + * @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, +extern int callback(struct libwebsocket *wsi, + enum libwebsocket_callback_reasons reason, void *user, void *in, size_t len); /** - * struct libwebsocket_protocols - List of protocols and handlers server - * supports. + * 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 + * 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 + * 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 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. + * 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 + * 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) + * 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() - * allows as many protocols as you like to be handled by one server. + * 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 { @@ -129,7 +129,7 @@ struct libwebsocket_protocols { * 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; @@ -142,6 +142,9 @@ libwebsocket_create_context(int port, const char *ssl_cert_filepath, const char *ssl_private_key_filepath, int gid, int uid); +extern void +libwebsocket_context_destroy(struct libwebsocket_context *this); + extern int libwebsockets_fork_service_loop(struct libwebsocket_context *this); @@ -188,7 +191,7 @@ libwebsockets_serve_http_file(struct libwebsocket *wsi, const char *file, /* notice - you need the pre- and post- padding allocation for buf below */ extern int -libwebsockets_broadcast(const struct libwebsocket_protocols * protocol, +libwebsockets_broadcast(const struct libwebsocket_protocols *protocol, unsigned char *buf, size_t len); extern const struct libwebsocket_protocols * diff --git a/lib/parsers.c b/lib/parsers.c index aafa38d..9d9efb3 100644 --- a/lib/parsers.c +++ b/lib/parsers.c @@ -149,7 +149,7 @@ int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c) debug("known hdr '%s'\n", wsi->name_buffer); wsi->parser_state = WSI_TOKEN_GET_URI + n; wsi->current_alloc_len = LWS_INITIAL_HDR_ALLOC; - + wsi->utf8_token[wsi->parser_state].token = malloc(wsi->current_alloc_len); wsi->utf8_token[wsi->parser_state].token_len = 0; @@ -220,7 +220,7 @@ int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c) return 0; } -static unsigned char inline +static inline unsigned char xor_mask(struct libwebsocket *wsi, unsigned char c) { c ^= wsi->masking_key_04[wsi->frame_mask_index++]; @@ -286,7 +286,7 @@ static int libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c) * wsi->frame_mask_04 will be our recirculating 20-byte XOR key * for this frame */ - + SHA1((unsigned char *)buf, 4 + 20, wsi->frame_mask_04); /* @@ -295,7 +295,7 @@ static int libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c) */ wsi->frame_mask_index = 0; - + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_1; break; @@ -305,7 +305,7 @@ static int libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c) * * We ignore the possibility of extension data because we don't * negotiate any extensions at the moment. - * + * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-------+-+-------------+-------------------------------+ @@ -334,23 +334,24 @@ static int libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c) * 04 spec defines the opcode like this: (1, 2, and 3 are * "control frame" opcodes which may not be fragmented or * have size larger than 126) - * + * * frame-opcode = - * %x0 ; continuation frame - * / %x1 ; connection close - * / %x2 ; ping - * / %x3 ; pong - * / %x4 ; text frame - * / %x5 ; binary frame - * / %x6-F ; reserved + * %x0 ; continuation frame + * / %x1 ; connection close + * / %x2 ; ping + * / %x3 ; pong + * / %x4 ; text frame + * / %x5 ; binary frame + * / %x6-F ; reserved * - * FIN (b7) + * FIN (b7) */ c = xor_mask(wsi, c); if (c & 0x70) { - fprintf(stderr, "Frame has extensions set illegally 1\n"); + fprintf(stderr, + "Frame has extensions set illegally 1\n"); /* kill the connection */ return -1; } @@ -412,7 +413,6 @@ static int libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c) wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8; break; default: -// fprintf(stderr, "seen incoming 04 frame len %d\n", c); wsi->rx_packet_length = c; wsi->lws_rx_parse_state = LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; @@ -605,51 +605,51 @@ int libwebsocket_client_rx_sm(struct libwebsocket *wsi, unsigned char c) } break; case 4: - /* - * 04 logical framing from the spec (all this is masked when incoming - * and has to be unmasked) - * - * We ignore the possibility of extension data because we don't - * negotiate any extensions at the moment. - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-------+-+-------------+-------------------------------+ - * |F|R|R|R| opcode|R| Payload len | Extended payload length | - * |I|S|S|S| (4) |S| (7) | (16/63) | - * |N|V|V|V| |V| | (if payload len==126/127) | - * | |1|2|3| |4| | | - * +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + - * | Extended payload length continued, if payload len == 127 | - * + - - - - - - - - - - - - - - - +-------------------------------+ - * | | Extension data | - * +-------------------------------+ - - - - - - - - - - - - - - - + - * : : - * +---------------------------------------------------------------+ - * : Application data : - * +---------------------------------------------------------------+ - * - * We pass payload through to userland as soon as we get it, ignoring - * FIN. It's up to userland to buffer it up if it wants to see a - * whole unfragmented block of the original size (which may be up to - * 2^63 long!) - */ + /* + * 04 logical framing from the spec (all this is masked when + * incoming and has to be unmasked) + * + * We ignore the possibility of extension data because we don't + * negotiate any extensions at the moment. + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-------+-+-------------+-------------------------------+ + * |F|R|R|R| opcode|R| Payload len | Extended payload length | + * |I|S|S|S| (4) |S| (7) | (16/63) | + * |N|V|V|V| |V| | (if payload len==126/127) | + * | |1|2|3| |4| | | + * +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + + * | Extended payload length continued, if payload len == 127 | + * + - - - - - - - - - - - - - - - +-------------------------------+ + * | | Extension data | + * +-------------------------------+ - - - - - - - - - - - - - - - + + * : : + * +---------------------------------------------------------------+ + * : Application data : + * +---------------------------------------------------------------+ + * + * We pass payload through to userland as soon as we get it, ignoring + * FIN. It's up to userland to buffer it up if it wants to see a + * whole unfragmented block of the original size (which may be up to + * 2^63 long!) + */ /* * 04 spec defines the opcode like this: (1, 2, and 3 are * "control frame" opcodes which may not be fragmented or * have size larger than 126) - * + * * frame-opcode = - * %x0 ; continuation frame - * / %x1 ; connection close - * / %x2 ; ping - * / %x3 ; pong - * / %x4 ; text frame - * / %x5 ; binary frame - * / %x6-F ; reserved + * %x0 ; continuation frame + * / %x1 ; connection close + * / %x2 ; ping + * / %x3 ; pong + * / %x4 ; text frame + * / %x5 ; binary frame + * / %x6-F ; reserved * - * FIN (b7) + * FIN (b7) */ if (c & 0x70) { @@ -1136,14 +1136,14 @@ send_raw: if (use_ssl) { n = SSL_write(wsi->ssl, buf - pre, len + pre + post); if (n < 0) { - fprintf(stderr, "ERROR writing to socket"); + fprintf(stderr, "ERROR writing to socket\n"); return -1; } } else { #endif - n = send(wsi->sock, buf - pre, len + pre + post, 0); + n = send(wsi->sock, buf - pre, len + pre + post, MSG_NOSIGNAL); if (n < 0) { - fprintf(stderr, "ERROR writing to socket"); + fprintf(stderr, "ERROR writing to socket\n"); return -1; } #ifdef LWS_OPENSSL_SUPPORT @@ -1211,7 +1211,7 @@ int libwebsockets_serve_http_file(struct libwebsocket *wsi, const char *file, /** * libwebsockets_remaining_packet_payload() - Bytes to come before "overall" - * rx packet is complete + * rx packet is complete * @wsi: Websocket instance (available from user callback) * * This function is intended to be called from the callback if the diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 6f35a1e..a263d83 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -51,16 +51,20 @@ #include #include "libwebsockets.h" -//#define DEBUG - +#if 0 +#define DEBUG +#endif #ifdef DEBUG -static inline void debug(const char *format, ...) { +static inline void debug(const char *format, ...) +{ va_list ap; va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); } #else -static inline void debug(const char *format, ...) { } +static inline void debug(const char *format, ...) +{ +} #endif #ifdef LWS_OPENSSL_SUPPORT diff --git a/libwebsockets-api-doc.html b/libwebsockets-api-doc.html index 6def9b3..c8a3477 100644 --- a/libwebsockets-api-doc.html +++ b/libwebsockets-api-doc.html @@ -1,3 +1,63 @@ +

libwebsocket_context_destroy - Destroy the websocket context

+void +libwebsocket_context_destroy +(struct libwebsocket_context * this) +

Arguments

+
+
this +
Websocket context +
+

Description

+
+This function closes any active connections and then frees the +context. After calling this, any further use of the context is +undefined. +
+
+

libwebsocket_service - Service any pending websocket activity

+int +libwebsocket_service +(struct libwebsocket_context * this, +int timeout_ms) +

Arguments

+
+
this +
Websocket context +
timeout_ms +
Timeout for poll; 0 means return immediately if nothing needed +service otherwise block and service immediately, returning +after the timeout if nothing needed service. +
+

Description

+
+This function deals with any pending websocket traffic, for three +kinds of event. It handles these events on both server and client +types of connection the same. +

+1) Accept new connections to our context's server +

+2) Perform pending broadcast writes initiated from other forked +processes (effectively serializing asynchronous broadcasts) +

+3) Call the receive callback for incoming frame data received by +server or client connections. +

+You need to call this service function periodically to all the above +functions to happen; if your application is single-threaded you can +just call it in your main event loop. +

+Alternatively you can fork a new process that asynchronously handles +calling this service in a loop. In that case you are happy if this +call blocks your thread until it needs to take care of something and +would call it with a large nonzero timeout. Your loop then takes no +CPU while there is nothing happening. +

+If you are calling it in a single-threaded app, you don't want it to +wait around blocking other things in your loop from happening, so you +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_create_context - Create the websocket handler

struct libwebsocket_context * libwebsocket_create_context diff --git a/test-server/test-client.c b/test-server/test-client.c index 6869d6d..3243ccd 100644 --- a/test-server/test-client.c +++ b/test-server/test-client.c @@ -380,6 +380,8 @@ int main(int argc, char **argv) libwebsocket_client_close(wsi_dumb); libwebsocket_client_close(wsi_mirror); + libwebsocket_context_destroy(context); + return 0; usage: diff --git a/test-server/test-server.c b/test-server/test-server.c index dc3cfd9..832a45a 100644 --- a/test-server/test-server.c +++ b/test-server/test-server.c @@ -1,7 +1,7 @@ /* * libwebsockets-test-server - libwebsockets test implementation * - * Copyright (C) 2010 Andy Green + * Copyright (C) 2010-2011 Andy Green * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -233,7 +233,7 @@ int main(int argc, char **argv) struct libwebsocket_context *context; fprintf(stderr, "libwebsockets test server\n" - "(C) Copyright 2010 Andy Green " + "(C) Copyright 2010-2011 Andy Green " "licensed under LGPL2.1\n"); while (n >= 0) { @@ -349,5 +349,7 @@ int main(int argc, char **argv) #endif + libwebsocket_context_destroy(context); + return 0; }