android fix rlimit
[platform/upstream/libwebsockets.git] / lib / context.c
index 0f4e1c4..68583ab 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * 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
@@ -34,7 +34,6 @@ static const char *library_version = LWS_LIBRARY_VERSION " " LWS_BUILD_HASH;
  *     representing the library version followed by the git head hash it
  *     was built from
  */
-
 LWS_VISIBLE const char *
 lws_get_library_version(void)
 {
@@ -42,17 +41,17 @@ 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
@@ -70,19 +69,24 @@ lws_get_library_version(void)
  *     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");
@@ -91,179 +95,205 @@ libwebsocket_create_context(struct lws_context_creation_info *info)
 #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;
 
@@ -280,50 +310,40 @@ libwebsocket_create_context(struct lws_context_creation_info *info)
        /* 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
@@ -331,70 +351,84 @@ bail:
  *     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);
 }