struct lws * lws_client_connect_via_info2(struct lws *wsi) { struct client_info_stash *stash = wsi->u.hdr.stash; if (!stash) return wsi; /* * we're not necessarily in a position to action these right away, * stash them... we only need during connect phase so u.hdr is fine */ if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, stash->address)) goto bail1; /* these only need u.hdr lifetime as well */ if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, stash->path)) goto bail1; if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, stash->host)) goto bail1; if (stash->origin[0]) if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ORIGIN, stash->origin)) goto bail1; /* * this is a list of protocols we tell the server we're okay with * stash it for later when we compare server response with it */ if (stash->protocol[0]) if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, stash->protocol)) goto bail1; if (stash->method[0]) if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_METHOD, stash->method)) goto bail1; if (stash->iface[0]) if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_IFACE, stash->iface)) goto bail1; #if defined(LWS_WITH_SOCKS5) if (!wsi->vhost->socks_proxy_port) lws_free_set_NULL(wsi->u.hdr.stash); #endif /* * Check with each extension if it is able to route and proxy this * connection for us. For example, an extension like x-google-mux * can handle this and then we don't need an actual socket for this * connection. */ if (lws_ext_cb_all_exts(wsi->context, wsi, LWS_EXT_CB_CAN_PROXY_CLIENT_CONNECTION, (void *)stash->address, wsi->c_port) > 0) { lwsl_client("lws_client_connect: ext handling conn\n"); lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_EXTENSION_CONNECT_RESPONSE, AWAITING_TIMEOUT); wsi->mode = LWSCM_WSCL_WAITING_EXTENSION_CONNECT; return wsi; } lwsl_client("lws_client_connect: direct conn\n"); wsi->context->count_wsi_allocated++; return lws_client_connect_2(wsi); bail1: #if defined(LWS_WITH_SOCKS5) if (!wsi->vhost->socks_proxy_port) lws_free_set_NULL(wsi->u.hdr.stash); #endif return NULL; }
char * lws_generate_client_handshake(struct lws *wsi, char *pkt) { char buf[128], hash[20], key_b64[40], *p = pkt; struct lws_context *context = wsi->context; int n; #ifndef LWS_NO_EXTENSIONS const struct lws_extension *ext; int ext_count = 0; #endif /* * create the random key */ n = lws_get_random(context, hash, 16); if (n != 16) { lwsl_err("Unable to read from random dev %s\n", SYSTEM_RANDOM_FILEPATH); lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); return NULL; } lws_b64_encode_string(hash, 16, key_b64, sizeof(key_b64)); /* * 00 example client handshake * * GET /socket.io/websocket HTTP/1.1 * Upgrade: WebSocket * Connection: Upgrade * Host: 127.0.0.1:9999 * Origin: http://127.0.0.1 * Sec-WebSocket-Key1: 1 0 2#0W 9 89 7 92 ^ * Sec-WebSocket-Key2: 7 7Y 4328 B2v[8(z1 * Cookie: socketio=websocket * * (Á®Ä0¶†≥ * * 04 example client handshake * * GET /chat HTTP/1.1 * Host: server.example.com * Upgrade: websocket * Connection: Upgrade * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== * Sec-WebSocket-Origin: http://example.com * Sec-WebSocket-Protocol: chat, superchat * Sec-WebSocket-Version: 4 */ p += sprintf(p, "GET %s HTTP/1.1\x0d\x0a", lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI)); p += sprintf(p, "Pragma: no-cache\x0d\x0a" "Cache-Control: no-cache\x0d\x0a"); p += sprintf(p, "Host: %s\x0d\x0a", lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST)); p += sprintf(p, "Upgrade: websocket\x0d\x0a" "Connection: Upgrade\x0d\x0a" "Sec-WebSocket-Key: "); strcpy(p, key_b64); p += strlen(key_b64); p += sprintf(p, "\x0d\x0a"); if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)) p += sprintf(p, "Origin: http://%s\x0d\x0a", lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)); if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS)) p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a", lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS)); /* tell the server what extensions we could support */ p += sprintf(p, "Sec-WebSocket-Extensions: "); #ifndef LWS_NO_EXTENSIONS ext = context->extensions; while (ext && ext->callback) { n = lws_ext_cb_all_exts(context, wsi, LWS_EXT_CB_CHECK_OK_TO_PROPOSE_EXTENSION, (char *)ext->name, 0); if (n) { /* an extension vetos us */ lwsl_ext("ext %s vetoed\n", (char *)ext->name); ext++; continue; } n = context->protocols[0].callback(wsi, LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED, wsi->user_space, (char *)ext->name, 0); /* * zero return from callback means * go ahead and allow the extension, * it's what we get if the callback is * unhandled */ if (n) { ext++; continue; } /* apply it */ if (ext_count) *p++ = ','; p += sprintf(p, "%s", ext->client_offer); ext_count++; ext++; } #endif p += sprintf(p, "\x0d\x0a"); if (wsi->ietf_spec_revision) p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a", wsi->ietf_spec_revision); /* give userland a chance to append, eg, cookies */ context->protocols[0].callback(wsi, LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER, NULL, &p, (pkt + LWS_MAX_SOCKET_IO_BUF) - p - 12); p += sprintf(p, "\x0d\x0a"); /* prepare the expected server accept response */ key_b64[39] = '\0'; /* enforce composed length below buf sizeof */ n = sprintf(buf, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", key_b64); lws_SHA1((unsigned char *)buf, n, (unsigned char *)hash); lws_b64_encode_string(hash, 20, wsi->u.hdr.ah->initial_handshake_hash_base64, sizeof(wsi->u.hdr.ah->initial_handshake_hash_base64)); return p; }
char * lws_generate_client_handshake(struct lws *wsi, char *pkt) { char buf[128], hash[20], key_b64[40], *p = pkt; struct lws_context *context = wsi->context; const char *meth; int n; #ifndef LWS_NO_EXTENSIONS const struct lws_extension *ext; int ext_count = 0; #endif const char *pp = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS); meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD); if (!meth) { meth = "GET"; wsi->do_ws = 1; } else { wsi->do_ws = 0; } if (!strcmp(meth, "RAW")) { lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); lwsl_notice("client transition to raw\n"); if (pp) { const struct lws_protocols *pr; pr = lws_vhost_name_to_protocol(wsi->vhost, pp); if (!pr) { lwsl_err("protocol %s not enabled on vhost\n", pp); return NULL; } lws_bind_protocol(wsi, pr); } if ((wsi->protocol->callback)(wsi, LWS_CALLBACK_RAW_ADOPT, wsi->user_space, NULL, 0)) return NULL; lws_header_table_force_to_detachable_state(wsi); lws_union_transition(wsi, LWSCM_RAW); lws_header_table_detach(wsi, 1); return NULL; } if (wsi->do_ws) { /* * create the random key */ n = lws_get_random(context, hash, 16); if (n != 16) { lwsl_err("Unable to read from random dev %s\n", SYSTEM_RANDOM_FILEPATH); lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); return NULL; } lws_b64_encode_string(hash, 16, key_b64, sizeof(key_b64)); } /* * 04 example client handshake * * GET /chat HTTP/1.1 * Host: server.example.com * Upgrade: websocket * Connection: Upgrade * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== * Sec-WebSocket-Origin: http://example.com * Sec-WebSocket-Protocol: chat, superchat * Sec-WebSocket-Version: 4 */ p += sprintf(p, "%s %s HTTP/1.1\x0d\x0a", meth, lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI)); p += sprintf(p, "Pragma: no-cache\x0d\x0a" "Cache-Control: no-cache\x0d\x0a"); p += sprintf(p, "Host: %s\x0d\x0a", lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST)); if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)) { if (lws_check_opt(context->options, LWS_SERVER_OPTION_JUST_USE_RAW_ORIGIN)) p += sprintf(p, "Origin: %s\x0d\x0a", lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)); else p += sprintf(p, "Origin: http://%s\x0d\x0a", lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)); } if (wsi->do_ws) { p += sprintf(p, "Upgrade: websocket\x0d\x0a" "Connection: Upgrade\x0d\x0a" "Sec-WebSocket-Key: "); strcpy(p, key_b64); p += strlen(key_b64); p += sprintf(p, "\x0d\x0a"); if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS)) p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a", lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS)); /* tell the server what extensions we could support */ #ifndef LWS_NO_EXTENSIONS ext = wsi->vhost->extensions; while (ext && ext->callback) { n = lws_ext_cb_all_exts(context, wsi, LWS_EXT_CB_CHECK_OK_TO_PROPOSE_EXTENSION, (char *)ext->name, 0); if (n) { /* an extension vetos us */ lwsl_ext("ext %s vetoed\n", (char *)ext->name); ext++; continue; } n = wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED, wsi->user_space, (char *)ext->name, 0); /* * zero return from callback means * go ahead and allow the extension, * it's what we get if the callback is * unhandled */ if (n) { ext++; continue; } /* apply it */ if (ext_count) *p++ = ','; else p += sprintf(p, "Sec-WebSocket-Extensions: "); p += sprintf(p, "%s", ext->client_offer); ext_count++; ext++; } if (ext_count) p += sprintf(p, "\x0d\x0a"); #endif if (wsi->ietf_spec_revision) p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a", wsi->ietf_spec_revision); /* prepare the expected server accept response */ key_b64[39] = '\0'; /* enforce composed length below buf sizeof */ n = sprintf(buf, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", key_b64); lws_SHA1((unsigned char *)buf, n, (unsigned char *)hash); lws_b64_encode_string(hash, 20, wsi->u.hdr.ah->initial_handshake_hash_base64, sizeof(wsi->u.hdr.ah->initial_handshake_hash_base64)); } /* give userland a chance to append, eg, cookies */ wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER, wsi->user_space, &p, (pkt + context->pt_serv_buf_size) - p - 12); p += sprintf(p, "\x0d\x0a"); return p; }
LWS_VISIBLE struct lws * lws_client_connect_via_info(struct lws_client_connect_info *i) { struct lws *wsi; int v = SPEC_LATEST_SUPPORTED; wsi = lws_zalloc(sizeof(struct lws)); if (wsi == NULL) goto bail; wsi->context = i->context; wsi->sock = LWS_SOCK_INVALID; /* -1 means just use latest supported */ if (i->ietf_version_or_minus_one != -1 && i->ietf_version_or_minus_one) v = i->ietf_version_or_minus_one; wsi->ietf_spec_revision = v; wsi->user_space = NULL; wsi->state = LWSS_CLIENT_UNCONNECTED; wsi->protocol = NULL; wsi->pending_timeout = NO_PENDING_TIMEOUT; #ifdef LWS_OPENSSL_SUPPORT wsi->use_ssl = i->ssl_connection; #else if (i->ssl_connection) { lwsl_err("libwebsockets not configured for ssl\n"); goto bail; } #endif if (lws_allocate_header_table(wsi)) goto bail; /* * we're not necessarily in a position to action these right away, * stash them... we only need during connect phase so u.hdr is fine */ wsi->u.hdr.ah->c_port = i->port; if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, i->address)) goto bail1; /* these only need u.hdr lifetime as well */ if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, i->path)) goto bail1; if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, i->host)) goto bail1; if (i->origin) if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ORIGIN, i->origin)) goto bail1; /* * this is a list of protocols we tell the server we're okay with * stash it for later when we compare server response with it */ if (i->protocol) if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, i->protocol)) goto bail1; wsi->protocol = &i->context->protocols[0]; if (wsi && !wsi->user_space && i->userdata) { wsi->user_space_externally_allocated = 1; wsi->user_space = i->userdata; } /* * Check with each extension if it is able to route and proxy this * connection for us. For example, an extension like x-google-mux * can handle this and then we don't need an actual socket for this * connection. */ if (lws_ext_cb_all_exts(i->context, wsi, LWS_EXT_CB_CAN_PROXY_CLIENT_CONNECTION, (void *)i->address, i->port) > 0) { lwsl_client("lws_client_connect: ext handling conn\n"); lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_EXTENSION_CONNECT_RESPONSE, AWAITING_TIMEOUT); wsi->mode = LWSCM_WSCL_WAITING_EXTENSION_CONNECT; return wsi; } lwsl_client("lws_client_connect: direct conn\n"); return lws_client_connect_2(wsi); bail1: lws_free_header_table(wsi); bail: lws_free(wsi); return NULL; }
LWS_VISIBLE struct lws_context * lws_create_context(struct lws_context_creation_info *info) { struct lws_context *context = NULL; struct lws wsi; #ifndef LWS_NO_DAEMONIZE int pid_daemon = get_daemonize_pid(); #endif char *p; int n; lwsl_notice("Initial logging level %d\n", log_level); 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 compiled in but disabled\n"); #else lwsl_notice("IPV6 not compiled in\n"); #endif 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); lwsl_info(" SPEC_LATEST_SUPPORTED: %u\n", SPEC_LATEST_SUPPORTED); lwsl_info(" AWAITING_TIMEOUT: %u\n", AWAITING_TIMEOUT); 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 = lws_zalloc(sizeof(struct lws_context)); if (!context) { lwsl_err("No memory for websocket context\n"); return NULL; } #ifndef LWS_NO_DAEMONIZE if (pid_daemon) { context->started_with_parent = pid_daemon; lwsl_notice(" Started with daemon pid %d\n", pid_daemon); } #endif context->lserv_seen = 0; 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; context->ka_time = info->ka_time; context->ka_interval = info->ka_interval; context->ka_probes = info->ka_probes; memset(&wsi, 0, sizeof(wsi)); wsi.context = context; 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_sigint_cb; #endif /* LWS_USE_LIBEV */ lwsl_info(" mem: context: %5u bytes\n", sizeof(struct lws_context)); /* * allocate and initialize the pool of * allocated_header structs + data */ if (info->max_http_header_data) context->max_http_header_data = info->max_http_header_data; else context->max_http_header_data = LWS_MAX_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_MAX_HEADER_POOL; context->http_header_data = lws_malloc(context->max_http_header_data * context->max_http_header_pool); if (!context->http_header_data) goto bail; context->ah_pool = lws_zalloc(sizeof(struct allocated_headers) * context->max_http_header_pool); if (!context->ah_pool) goto bail; for (n = 0; n < context->max_http_header_pool; n++) context->ah_pool[n].data = (char *)context->http_header_data + (n * context->max_http_header_data); /* this is per context */ lwsl_info(" mem: http hdr rsvd: %5u bytes ((%u + %u) x %u)\n", (context->max_http_header_data + sizeof(struct allocated_headers)) * context->max_http_header_pool, context->max_http_header_data, sizeof(struct allocated_headers), context->max_http_header_pool); context->max_fds = getdtablesize(); context->fds = lws_zalloc(sizeof(struct lws_pollfd) * context->max_fds); if (context->fds == NULL) { lwsl_err("OOM allocating %d fds\n", context->max_fds); goto bail; } lwsl_info(" mem: pollfd map: %5u\n", sizeof(struct lws_pollfd) * context->max_fds); if (lws_plat_init(context, info)) goto bail; lws_context_init_extensions(info, context); context->user_space = info->user; lwsl_notice(" mem: per-conn: %5u bytes + protocol rx buf\n", sizeof(struct lws)); 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; if (lws_context_init_server(info, context)) goto bail; /* * drop any root privs for this process * to listen on port < 1023 we would have needed root, but now we are * listening, we don't want the power for anything else */ lws_plat_drop_app_privileges(info); /* initialize supported protocols */ for (context->count_protocols = 0; 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. * * 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(&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_cb_all_exts(context, NULL, LWS_EXT_CALLBACK_SERVER_CONTEXT_CONSTRUCT, NULL, 0) < 0) goto bail; } else if (lws_ext_cb_all_exts(context, NULL, LWS_EXT_CALLBACK_CLIENT_CONTEXT_CONSTRUCT, NULL, 0) < 0) goto bail; return context; bail: lws_context_destroy(context); return NULL; }
/** * lws_context_destroy() - Destroy the websocket context * @context: Websocket context * * This function closes any active connections and then frees the * context. After calling this, any further use of the context is * undefined. */ LWS_VISIBLE void lws_context_destroy(struct lws_context *context) { const struct lws_protocols *protocol = NULL; struct lws wsi; int n; lwsl_notice("%s\n", __func__); if (!context) return; 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 lws *wsi = wsi_from_fd(context, context->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 */ n = lws_ext_cb_all_exts(context, NULL, LWS_EXT_CALLBACK_SERVER_CONTEXT_DESTRUCT, NULL, 0); n = lws_ext_cb_all_exts(context, NULL, LWS_EXT_CALLBACK_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++; } } #ifdef LWS_USE_LIBEV ev_io_stop(context->io_loop, &context->w_accept.watcher); if(context->use_ev_sigint) ev_signal_stop(context->io_loop, &context->w_sigint.watcher); #endif /* LWS_USE_LIBEV */ lws_plat_context_early_destroy(context); lws_ssl_context_destroy(context); if (context->fds) lws_free(context->fds); if (context->ah_pool) lws_free(context->ah_pool); if (context->http_header_data) lws_free(context->http_header_data); lws_plat_context_late_destroy(context); lws_free(context); }
LWS_VISIBLE void lws_context_destroy(struct lws_context *context) { const struct lws_protocols *protocol = NULL; struct lws_context_per_thread *pt; struct lws_vhost *vh = NULL, *vh1; 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 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--; } lws_pt_mutex_destroy(pt); } /* * give all extensions a chance to clean up any per-context * allocations they might have made */ 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. * * We can't free things until after the event loop shuts down. */ if (context->protocol_init_done) vh = context->vhost_list; while (vh) { wsi.vhost = vh; protocol = vh->protocols; if (protocol) { n = 0; while (n < vh->count_protocols) { wsi.protocol = protocol; protocol->callback(&wsi, LWS_CALLBACK_PROTOCOL_DESTROY, NULL, NULL, 0); protocol++; n++; } } vh = vh->vhost_next; } for (n = 0; n < context->count_threads; n++) { pt = &context->pt[n]; lws_libev_destroyloop(context, n); lws_libuv_destroyloop(context, n); 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); /* free all the vhost allocations */ vh = context->vhost_list; while (vh) { protocol = vh->protocols; if (protocol) { n = 0; while (n < vh->count_protocols) { if (vh->protocol_vh_privs && vh->protocol_vh_privs[n]) { lws_free(vh->protocol_vh_privs[n]); vh->protocol_vh_privs[n] = NULL; } protocol++; n++; } } if (vh->protocol_vh_privs) lws_free(vh->protocol_vh_privs); lws_ssl_SSL_CTX_destroy(vh); lws_free(vh->same_vh_protocol_list); #ifdef LWS_WITH_PLUGINS if (context->plugin_list) lws_free((void *)vh->protocols); #ifndef LWS_NO_EXTENSIONS if (context->plugin_extension_count) lws_free((void *)vh->extensions); #endif #endif #ifdef LWS_WITH_ACCESS_LOG if (vh->log_fd != (int)LWS_INVALID_FILE) close(vh->log_fd); #endif vh1 = vh->vhost_next; lws_free(vh); vh = vh1; } lws_plat_context_late_destroy(context); lws_free(context); }
LWS_VISIBLE struct lws_context * lws_create_context(struct lws_context_creation_info *info) { struct lws_context *context = NULL; #ifndef LWS_NO_DAEMONIZE int pid_daemon = get_daemonize_pid(); #endif int n, m; #if defined(__ANDROID__) struct rlimit rt; #endif lwsl_notice("Initial logging level %d\n", log_level); lwsl_notice("Libwebsockets version: %s\n", library_version); #if LWS_POSIX #ifdef LWS_USE_IPV6 if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DISABLE_IPV6)) lwsl_notice("IPV6 compiled in and enabled\n"); else lwsl_notice("IPV6 compiled in but disabled\n"); #else lwsl_notice("IPV6 not compiled in\n"); #endif lws_feature_status_libev(info); lws_feature_status_libuv(info); #endif 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); #endif if (lws_plat_context_early_init()) return NULL; context = lws_zalloc(sizeof(struct lws_context)); if (!context) { lwsl_err("No memory for websocket context\n"); return NULL; } if (info->pt_serv_buf_size) context->pt_serv_buf_size = info->pt_serv_buf_size; else context->pt_serv_buf_size = 4096; context->reject_service_keywords = info->reject_service_keywords; context->time_up = time(NULL); #ifndef LWS_NO_DAEMONIZE 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 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->token_limits = info->token_limits; context->options = info->options; if (info->timeout_secs) context->timeout_secs = info->timeout_secs; else context->timeout_secs = AWAITING_TIMEOUT; context->ws_ping_pong_interval = info->ws_ping_pong_interval; lwsl_info(" default timeout (secs): %u\n", context->timeout_secs); if (info->max_http_header_data) context->max_http_header_data = info->max_http_header_data; else if (info->max_http_header_data2) context->max_http_header_data = info->max_http_header_data2; 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(context->pt_serv_buf_size); if (!context->pt[n].serv_buf) { lwsl_err("OOM\n"); return NULL; } #ifdef LWS_USE_LIBUV context->pt[n].context = context; #endif 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]); } 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; lwsl_notice(" Threads: %d each %d fds\n", context->count_threads, context->fd_limit_per_thread); memset(&wsi, 0, sizeof(wsi)); wsi.context = context; 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 lwsl_info(" mem: context: %5u bytes (%d ctx + (%d thr x %d))\n", sizeof(struct lws_context) + (context->count_threads * context->pt_serv_buf_size), sizeof(struct lws_context), context->count_threads, context->pt_serv_buf_size); 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->server_string) { context->server_string = info->server_string; context->server_string_len = (short) strlen(context->server_string); } else { context->server_string = "libwebsockets"; context->server_string_len = 13; } #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 (lws_plat_init(context, info)) goto bail; lws_context_init_ssl_library(info); context->user_space = info->user; /* * if he's not saying he'll make his own vhosts later then act * compatibly and make a default vhost using the data in the info */ if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) if (!lws_create_vhost(context, info)) { lwsl_err("Failed to create default vhost\n"); return NULL; } lws_context_init_extensions(info, context); lwsl_notice(" mem: per-conn: %5u bytes + protocol rx buf\n", sizeof(struct lws)); strcpy(context->canonical_hostname, "unknown"); lws_server_get_canonical_hostname(context, info); context->uid = info->uid; context->gid = info->gid; /* * drop any root privs for this process * to listen on port < 1023 we would have needed root, but now we are * listening, we don't want the power for anything else */ if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) lws_plat_drop_app_privileges(info); /* * give all extensions a chance to create any per-context * allocations they need */ if (info->port != CONTEXT_PORT_NO_LISTEN) { if (lws_ext_cb_all_exts(context, NULL, LWS_EXT_CB_SERVER_CONTEXT_CONSTRUCT, NULL, 0) < 0) goto bail; } else if (lws_ext_cb_all_exts(context, NULL, LWS_EXT_CB_CLIENT_CONTEXT_CONSTRUCT, NULL, 0) < 0) goto bail; return context; bail: lws_context_destroy(context); return NULL; }
/** * lws_context_destroy() - Destroy the websocket context * @context: Websocket context * * This function closes any active connections and then frees the * context. After calling this, any further use of the context is * undefined. */ LWS_VISIBLE void lws_context_destroy(struct lws_context *context) { 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 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 */ 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++; } for (n = 0; n < context->count_threads; n++) { pt = &context->pt[n]; lws_libev_destroyloop(context, n); lws_libuv_destroyloop(context, n); 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); }