/*
* libwebsockets - small server side websockets and web server implementation
*
- * Copyright (C) 2010-2014 Andy Green <andy@warmcat.com>
+ * Copyright (C) 2010-2015 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* representing the library version followed by the git head hash it
* was built from
*/
-
LWS_VISIBLE const char *
lws_get_library_version(void)
{
}
/**
- * libwebsocket_create_context() - Create the websocket handler
+ * lws_create_context() - Create the websocket handler
* @info: pointer to struct with parameters
*
* This function creates the listening socket (if serving) and takes care
* of all initialization in one step.
*
- * After initialization, it returns a struct libwebsocket_context * that
+ * After initialization, it returns a struct lws_context * that
* represents this server. After calling, user code needs to take care
- * of calling libwebsocket_service() with the context pointer to get the
- * server's sockets serviced. This can be done in the same process context
- * or a forked process, or another thread,
+ * of calling lws_service() with the context pointer to get the
+ * server's sockets serviced. This must be done in the same process
+ * context as the initialization call.
*
* The protocol callback functions are called for a handful of events
* including http requests coming in, websocket connections becoming
* images or whatever over http and dynamic data over websockets all in
* one place; they're all handled in the user callback.
*/
-
-LWS_VISIBLE struct libwebsocket_context *
-libwebsocket_create_context(struct lws_context_creation_info *info)
+LWS_VISIBLE struct lws_context *
+lws_create_context(struct lws_context_creation_info *info)
{
- struct libwebsocket_context *context = NULL;
- char *p;
-
+ struct lws_context *context = NULL;
+ struct lws wsi;
#ifndef LWS_NO_DAEMONIZE
int pid_daemon = get_daemonize_pid();
#endif
+ char *p;
+ int n, m;
+#if defined(__ANDROID__)
+ struct rlimit rt;
+#endif
+
lwsl_notice("Initial logging level %d\n", log_level);
- lwsl_notice("Library version: %s\n", library_version);
+ lwsl_notice("Libwebsockets version: %s\n", library_version);
+#if LWS_POSIX
#ifdef LWS_USE_IPV6
if (!(info->options & LWS_SERVER_OPTION_DISABLE_IPV6))
lwsl_notice("IPV6 compiled in and enabled\n");
#else
lwsl_notice("IPV6 not compiled in\n");
#endif
-#ifdef LWS_USE_LIBEV
- if (info->options & LWS_SERVER_OPTION_LIBEV)
- lwsl_notice("libev support compiled in and enabled\n");
- else
- lwsl_notice("libev support compiled in but disabled\n");
-#else
- lwsl_notice("libev support not compiled in\n");
+ lws_feature_status_libev(info);
#endif
- lwsl_info(" LWS_MAX_HEADER_LEN: %u\n", LWS_MAX_HEADER_LEN);
- lwsl_info(" LWS_MAX_PROTOCOLS: %u\n", LWS_MAX_PROTOCOLS);
-#ifndef LWS_NO_EXTENSIONS
- lwsl_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n",
- LWS_MAX_EXTENSIONS_ACTIVE);
-#else
- lwsl_notice(" Configured without extension support\n");
-#endif
- lwsl_info(" SPEC_LATEST_SUPPORTED: %u\n", SPEC_LATEST_SUPPORTED);
- lwsl_info(" AWAITING_TIMEOUT: %u\n", AWAITING_TIMEOUT);
- if (info->ssl_cipher_list)
- lwsl_info(" SSL ciphers: '%s'\n", info->ssl_cipher_list);
+ lwsl_info(" LWS_DEF_HEADER_LEN : %u\n", LWS_DEF_HEADER_LEN);
+ lwsl_info(" LWS_MAX_PROTOCOLS : %u\n", LWS_MAX_PROTOCOLS);
+ lwsl_info(" LWS_MAX_SMP : %u\n", LWS_MAX_SMP);
+ lwsl_info(" SPEC_LATEST_SUPPORTED : %u\n", SPEC_LATEST_SUPPORTED);
+ lwsl_info(" sizeof (*info) : %u\n", sizeof(*info));
+#if LWS_POSIX
lwsl_info(" SYSTEM_RANDOM_FILEPATH: '%s'\n", SYSTEM_RANDOM_FILEPATH);
- lwsl_info(" LWS_MAX_ZLIB_CONN_BUFFER: %u\n", LWS_MAX_ZLIB_CONN_BUFFER);
-
+#endif
if (lws_plat_context_early_init())
return NULL;
- context = (struct libwebsocket_context *)
- malloc(sizeof(struct libwebsocket_context));
+ context = lws_zalloc(sizeof(struct lws_context));
if (!context) {
lwsl_err("No memory for websocket context\n");
return NULL;
}
- memset(context, 0, sizeof(*context));
#ifndef LWS_NO_DAEMONIZE
- context->started_with_parent = pid_daemon;
- lwsl_notice(" Started with daemon pid %d\n", pid_daemon);
+ if (pid_daemon) {
+ context->started_with_parent = pid_daemon;
+ lwsl_notice(" Started with daemon pid %d\n", pid_daemon);
+ }
+#endif
+#if defined(__ANDROID__)
+ n = getrlimit ( RLIMIT_NOFILE,&rt);
+ if (-1 == n) {
+ lwsl_err("Get RLIMIT_NOFILE failed!\n");
+ return NULL;
+ }
+ context->max_fds = rt.rlim_cur;
+#else
+ context->max_fds = getdtablesize();
#endif
- context->listen_service_extraseen = 0;
+ if (info->count_threads)
+ context->count_threads = info->count_threads;
+ else
+ context->count_threads = 1;
+
+ if (context->count_threads > LWS_MAX_SMP)
+ context->count_threads = LWS_MAX_SMP;
+
context->protocols = info->protocols;
+ context->token_limits = info->token_limits;
context->listen_port = info->port;
context->http_proxy_port = 0;
context->http_proxy_address[0] = '\0';
context->options = info->options;
context->iface = info->iface;
- /* to reduce this allocation, */
- context->max_fds = getdtablesize();
- lwsl_notice(" static allocation: %u + (%u x %u fds) = %u bytes\n",
- sizeof(struct libwebsocket_context),
- sizeof(struct libwebsocket_pollfd) +
- sizeof(struct libwebsocket *),
- context->max_fds,
- sizeof(struct libwebsocket_context) +
- ((sizeof(struct libwebsocket_pollfd) +
- sizeof(struct libwebsocket *)) *
- context->max_fds));
-
- context->fds = (struct libwebsocket_pollfd *)
- malloc(sizeof(struct libwebsocket_pollfd) *
- context->max_fds);
- if (context->fds == NULL) {
- lwsl_err("Unable to allocate fds array for %d connections\n",
- context->max_fds);
- free(context);
- return NULL;
- }
+ context->ka_time = info->ka_time;
+ context->ka_interval = info->ka_interval;
+ context->ka_probes = info->ka_probes;
+ if (info->timeout_secs)
+ context->timeout_secs = info->timeout_secs;
+ else
+ context->timeout_secs = AWAITING_TIMEOUT;
- context->lws_lookup = (struct libwebsocket **)
- malloc(sizeof(struct libwebsocket *) * context->max_fds);
- if (context->lws_lookup == NULL) {
- lwsl_err(
- "Unable to allocate lws_lookup array for %d connections\n",
- context->max_fds);
- free(context->fds);
- free(context);
- return NULL;
- }
- memset(context->lws_lookup, 0, sizeof(struct libwebsocket *) *
- context->max_fds);
+ lwsl_info(" default timeout (secs): %u\n", context->timeout_secs);
- if (lws_plat_init_fd_tables(context)) {
- free(context->lws_lookup);
- free(context->fds);
- free(context);
- return NULL;
+ if (info->max_http_header_data)
+ context->max_http_header_data = info->max_http_header_data;
+ else
+ context->max_http_header_data = LWS_DEF_HEADER_LEN;
+ if (info->max_http_header_pool)
+ context->max_http_header_pool = info->max_http_header_pool;
+ else
+ context->max_http_header_pool = LWS_DEF_HEADER_POOL;
+
+ /*
+ * Allocate the per-thread storage for scratchpad buffers,
+ * and header data pool
+ */
+ for (n = 0; n < context->count_threads; n++) {
+ context->pt[n].serv_buf = lws_zalloc(LWS_MAX_SOCKET_IO_BUF);
+ if (!context->pt[n].serv_buf) {
+ lwsl_err("OOM\n");
+ return NULL;
+ }
+
+ context->pt[n].context = context;
+ context->pt[n].tid = n;
+ context->pt[n].http_header_data = lws_malloc(context->max_http_header_data *
+ context->max_http_header_pool);
+ if (!context->pt[n].http_header_data)
+ goto bail;
+
+ context->pt[n].ah_pool = lws_zalloc(sizeof(struct allocated_headers) *
+ context->max_http_header_pool);
+ for (m = 0; m < context->max_http_header_pool; m++)
+ context->pt[n].ah_pool[m].data =
+ (char *)context->pt[n].http_header_data +
+ (m * context->max_http_header_data);
+ if (!context->pt[n].ah_pool)
+ goto bail;
+
+ lws_pt_mutex_init(&context->pt[n]);
}
-#ifndef LWS_NO_EXTENSIONS
- context->extensions = info->extensions;
-#endif
- context->user_space = info->user;
+ if (info->fd_limit_per_thread)
+ context->fd_limit_per_thread = info->fd_limit_per_thread;
+ else
+ context->fd_limit_per_thread = context->max_fds /
+ context->count_threads;
- strcpy(context->canonical_hostname, "unknown");
+ lwsl_notice(" Threads: %d each %d fds\n", context->count_threads,
+ context->fd_limit_per_thread);
-#ifndef LWS_NO_SERVER
- if (!(info->options & LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME)) {
- /* find canonical hostname */
- gethostname((char *)context->canonical_hostname,
- sizeof(context->canonical_hostname) - 1);
+ memset(&wsi, 0, sizeof(wsi));
+ wsi.context = context;
- lwsl_notice(" canonical_hostname = %s\n",
- context->canonical_hostname);
+ if (!info->ka_interval && info->ka_time > 0) {
+ lwsl_err("info->ka_interval can't be 0 if ka_time used\n");
+ return NULL;
}
+
+#ifdef LWS_USE_LIBEV
+ /* (Issue #264) In order to *avoid breaking backwards compatibility*, we
+ * enable libev mediated SIGINT handling with a default handler of
+ * lws_sigint_cb. The handler can be overridden or disabled
+ * by invoking lws_sigint_cfg after creating the context, but
+ * before invoking lws_initloop:
+ */
+ context->use_ev_sigint = 1;
+ context->lws_ev_sigint_cb = &lws_ev_sigint_cb;
+#endif /* LWS_USE_LIBEV */
+#ifdef LWS_USE_LIBUV
+ /* (Issue #264) In order to *avoid breaking backwards compatibility*, we
+ * enable libev mediated SIGINT handling with a default handler of
+ * lws_sigint_cb. The handler can be overridden or disabled
+ * by invoking lws_sigint_cfg after creating the context, but
+ * before invoking lws_initloop:
+ */
+ context->use_ev_sigint = 1;
+ context->lws_uv_sigint_cb = &lws_uv_sigint_cb;
#endif
- /* split the proxy ads:port if given */
+ lwsl_info(" mem: context: %5u bytes (%d ctx + (%d thr x %d))\n",
+ sizeof(struct lws_context) +
+ (context->count_threads * LWS_MAX_SOCKET_IO_BUF),
+ sizeof(struct lws_context),
+ context->count_threads,
+ LWS_MAX_SOCKET_IO_BUF);
+
+ lwsl_info(" mem: http hdr rsvd: %5u bytes (%u thr x (%u + %u) x %u))\n",
+ (context->max_http_header_data +
+ sizeof(struct allocated_headers)) *
+ context->max_http_header_pool * context->count_threads,
+ context->count_threads,
+ context->max_http_header_data,
+ sizeof(struct allocated_headers),
+ context->max_http_header_pool);
+ n = sizeof(struct lws_pollfd) * context->count_threads *
+ context->fd_limit_per_thread;
+ context->pt[0].fds = lws_zalloc(n);
+ if (context->pt[0].fds == NULL) {
+ lwsl_err("OOM allocating %d fds\n", context->max_fds);
+ goto bail;
+ }
+ lwsl_info(" mem: pollfd map: %5u\n", n);
- if (info->http_proxy_address) {
- strncpy(context->http_proxy_address, info->http_proxy_address,
- sizeof(context->http_proxy_address) - 1);
- context->http_proxy_address[
- sizeof(context->http_proxy_address) - 1] = '\0';
- context->http_proxy_port = info->http_proxy_port;
- } else {
-#ifdef HAVE_GETENV
- p = getenv("http_proxy");
- if (p) {
- strncpy(context->http_proxy_address, p,
- sizeof(context->http_proxy_address) - 1);
- context->http_proxy_address[
- sizeof(context->http_proxy_address) - 1] = '\0';
-
- p = strchr(context->http_proxy_address, ':');
- if (p == NULL) {
- lwsl_err("http_proxy needs to be ads:port\n");
- goto bail;
- }
- *p = '\0';
- context->http_proxy_port = atoi(p + 1);
- }
+#if LWS_MAX_SMP > 1
+ /* each thread serves his own chunk of fds */
+ for (n = 1; n < (int)info->count_threads; n++)
+ context->pt[n].fds = context->pt[n - 1].fds +
+ context->fd_limit_per_thread;
#endif
- }
- if (context->http_proxy_address[0])
- lwsl_notice(" Proxy %s:%u\n",
- context->http_proxy_address,
- context->http_proxy_port);
+ if (lws_plat_init(context, info))
+ goto bail;
-#ifndef LWS_NO_SERVER
- if (info->port != CONTEXT_PORT_NO_LISTEN) {
+ lws_context_init_extensions(info, context);
-#ifdef LWS_OPENSSL_SUPPORT
- context->use_ssl = info->ssl_cert_filepath != NULL &&
- info->ssl_private_key_filepath != NULL;
-#ifdef USE_CYASSL
- lwsl_notice(" Compiled with CYASSL support\n");
-#else
- lwsl_notice(" Compiled with OpenSSL support\n");
-#endif
- if (context->use_ssl)
- lwsl_notice(" Using SSL mode\n");
- else
- lwsl_notice(" Using non-SSL mode\n");
+ context->user_space = info->user;
-#else
- if (info->ssl_cert_filepath != NULL &&
- info->ssl_private_key_filepath != NULL) {
- lwsl_notice(" Not compiled for OpenSSl support!\n");
- goto bail;
- }
- lwsl_notice(" Compiled without SSL support\n");
-#endif
+ lwsl_notice(" mem: per-conn: %5u bytes + protocol rx buf\n",
+ sizeof(struct lws));
- lwsl_notice(
- " per-conn mem: %u + %u headers + protocol rx buf\n",
- sizeof(struct libwebsocket),
- sizeof(struct allocated_headers));
- }
+ strcpy(context->canonical_hostname, "unknown");
+ lws_server_get_canonical_hostname(context, info);
+
+ /* either use proxy from info, or try get it from env var */
+
+ if (info->http_proxy_address) {
+ /* override for backwards compatibility */
+ if (info->http_proxy_port)
+ context->http_proxy_port = info->http_proxy_port;
+ lws_set_proxy(context, info->http_proxy_address);
+ } else {
+#ifdef LWS_HAVE_GETENV
+ p = getenv("http_proxy");
+ if (p)
+ lws_set_proxy(context, p);
#endif
+ }
if (lws_context_init_server_ssl(info, context))
goto bail;
-
+
if (lws_context_init_client_ssl(info, context))
goto bail;
/* initialize supported protocols */
for (context->count_protocols = 0;
- info->protocols[context->count_protocols].callback;
- context->count_protocols++) {
-
- lwsl_parser(" Protocol: %s\n",
- info->protocols[context->count_protocols].name);
-
- info->protocols[context->count_protocols].owning_server =
- context;
- info->protocols[context->count_protocols].protocol_index =
- context->count_protocols;
-
+ info->protocols[context->count_protocols].callback;
+ context->count_protocols++)
/*
* inform all the protocols that they are doing their one-time
- * initialization if they want to
+ * initialization if they want to.
+ *
+ * NOTE the wsi is all zeros except for the context pointer
+ * so lws_get_context(wsi) can work in the callback.
*/
- info->protocols[context->count_protocols].callback(context,
- NULL, LWS_CALLBACK_PROTOCOL_INIT, NULL, NULL, 0);
- }
+ info->protocols[context->count_protocols].callback(&wsi,
+ LWS_CALLBACK_PROTOCOL_INIT, NULL, NULL, 0);
/*
* give all extensions a chance to create any per-context
* allocations they need
*/
-
if (info->port != CONTEXT_PORT_NO_LISTEN) {
- if (lws_ext_callback_for_each_extension_type(context, NULL,
- LWS_EXT_CALLBACK_SERVER_CONTEXT_CONSTRUCT,
- NULL, 0) < 0)
+ if (lws_ext_cb_all_exts(context, NULL,
+ LWS_EXT_CB_SERVER_CONTEXT_CONSTRUCT, NULL, 0) < 0)
goto bail;
} else
- if (lws_ext_callback_for_each_extension_type(context, NULL,
- LWS_EXT_CALLBACK_CLIENT_CONTEXT_CONSTRUCT,
- NULL, 0) < 0)
+ if (lws_ext_cb_all_exts(context, NULL,
+ LWS_EXT_CB_CLIENT_CONTEXT_CONSTRUCT, NULL, 0) < 0)
goto bail;
return context;
bail:
- libwebsocket_context_destroy(context);
+ lws_context_destroy(context);
return NULL;
}
/**
- * libwebsocket_context_destroy() - Destroy the websocket context
+ * lws_context_destroy() - Destroy the websocket context
* @context: Websocket context
*
* This function closes any active connections and then frees the
* undefined.
*/
LWS_VISIBLE void
-libwebsocket_context_destroy(struct libwebsocket_context *context)
+lws_context_destroy(struct lws_context *context)
{
- int n;
- struct libwebsocket_protocols *protocol = context->protocols;
+ const struct lws_protocols *protocol = NULL;
+ struct lws_context_per_thread *pt;
+ struct lws wsi;
+ int n, m;
+
+ lwsl_notice("%s\n", __func__);
+
+ if (!context)
+ return;
+
+ m = context->count_threads;
+ context->being_destroyed = 1;
+
+ memset(&wsi, 0, sizeof(wsi));
+ wsi.context = context;
#ifdef LWS_LATENCY
if (context->worst_latency_info[0])
lwsl_notice("Worst latency: %s\n", context->worst_latency_info);
#endif
- for (n = 0; n < context->fds_count; n++) {
- struct libwebsocket *wsi =
- context->lws_lookup[context->fds[n].fd];
- if (!wsi)
- continue;
- libwebsocket_close_and_free_session(context,
- wsi, LWS_CLOSE_STATUS_NOSTATUS /* no protocol close */);
- n--;
- }
+ while (m--) {
+ pt = &context->pt[m];
+
+ for (n = 0; (unsigned int)n < context->pt[m].fds_count; n++) {
+ struct lws *wsi = wsi_from_fd(context, pt->fds[n].fd);
+ if (!wsi)
+ continue;
+ lws_close_free_wsi(wsi,
+ LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY
+ /* no protocol close */);
+ n--;
+ }
+ }
/*
* give all extensions a chance to clean up any per-context
* allocations they might have made
*/
- if (context->listen_port) {
- if (lws_ext_callback_for_each_extension_type(context, NULL,
- LWS_EXT_CALLBACK_SERVER_CONTEXT_DESTRUCT, NULL, 0) < 0)
- return;
- } else
- if (lws_ext_callback_for_each_extension_type(context, NULL,
- LWS_EXT_CALLBACK_CLIENT_CONTEXT_DESTRUCT, NULL, 0) < 0)
- return;
+
+ n = lws_ext_cb_all_exts(context, NULL,
+ LWS_EXT_CB_SERVER_CONTEXT_DESTRUCT, NULL, 0);
+
+ n = lws_ext_cb_all_exts(context, NULL,
+ LWS_EXT_CB_CLIENT_CONTEXT_DESTRUCT, NULL, 0);
/*
* inform all the protocols that they are done and will have no more
* callbacks
*/
+ protocol = context->protocols;
+ if (protocol)
+ while (protocol->callback) {
+ protocol->callback(&wsi, LWS_CALLBACK_PROTOCOL_DESTROY,
+ NULL, NULL, 0);
+ protocol++;
+ }
- while (protocol->callback) {
- protocol->callback(context, NULL, LWS_CALLBACK_PROTOCOL_DESTROY,
- NULL, NULL, 0);
- protocol++;
- }
-
- lws_plat_context_early_destroy(context);
-
-#ifdef LWS_OPENSSL_SUPPORT
- if (context->ssl_ctx)
- SSL_CTX_free(context->ssl_ctx);
- if (context->ssl_client_ctx)
- SSL_CTX_free(context->ssl_client_ctx);
-
- ERR_remove_state(0);
- ERR_free_strings();
- EVP_cleanup();
- CRYPTO_cleanup_all_ex_data();
-#endif
+ for (n = 0; n < context->count_threads; n++) {
+ pt = &context->pt[n];
- if (context->fds)
- free(context->fds);
- if (context->lws_lookup)
- free(context->lws_lookup);
+ lws_libev_destroyloop(context, n);
+ lws_libuv_destroyloop(context, n);
- free(context);
+ lws_free_set_NULL(context->pt[n].serv_buf);
+ if (pt->ah_pool)
+ lws_free(pt->ah_pool);
+ if (pt->http_header_data)
+ lws_free(pt->http_header_data);
+ }
+ lws_plat_context_early_destroy(context);
+ lws_ssl_context_destroy(context);
+ if (context->pt[0].fds)
+ lws_free_set_NULL(context->pt[0].fds);
lws_plat_context_late_destroy(context);
+
+ lws_free(context);
}