introduce vhosts
This patch splits out some lws_context members into a new lws_vhost struct. - ssl state and options per vhost - SSL_CTX for serving and client per vhost - protocols[] per vhost - extensions[] per vhost lws_context maintains a linked list of lws_vhosts. The same lws_context_creation_info struct is used to regulate both the context creation and to create vhosts: for backward compatibility if you didn't provide the new LWS_SERVER_OPTION_EXPLICIT_VHOSTS option, then a default vhost is created at context creation time using the same info data as the context itself. If you will have multiple vhosts though, you should give the LWS_SERVER_OPTION_EXPLICIT_VHOSTS option at context creation time, create the context first and then the vhosts afterwards using lws_create_vhost(contest, &info); Although there is a lot of housekeeping to implement this change, there is almost no additional overhead if you don't use multiple vhosts and very little api impact (no changes to test apps). Signed-off-by: Andy Green <andy@warmcat.com>
This commit is contained in:
parent
e2cf3e1cc0
commit
d526c50c22
18 changed files with 784 additions and 370 deletions
|
@ -19,28 +19,28 @@ lws_client_connect_2(struct lws *wsi)
|
|||
|
||||
/* proxy? */
|
||||
|
||||
if (context->http_proxy_port) {
|
||||
if (wsi->vhost->http_proxy_port) {
|
||||
plen = sprintf((char *)pt->serv_buf,
|
||||
"CONNECT %s:%u HTTP/1.0\x0d\x0a"
|
||||
"User-agent: libwebsockets\x0d\x0a",
|
||||
lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS),
|
||||
wsi->u.hdr.c_port);
|
||||
|
||||
if (context->proxy_basic_auth_token[0])
|
||||
if (wsi->vhost->proxy_basic_auth_token[0])
|
||||
plen += sprintf((char *)pt->serv_buf + plen,
|
||||
"Proxy-authorization: basic %s\x0d\x0a",
|
||||
context->proxy_basic_auth_token);
|
||||
wsi->vhost->proxy_basic_auth_token);
|
||||
|
||||
plen += sprintf((char *)pt->serv_buf + plen, "\x0d\x0a");
|
||||
ads = context->http_proxy_address;
|
||||
ads = wsi->vhost->http_proxy_address;
|
||||
|
||||
#ifdef LWS_USE_IPV6
|
||||
if (LWS_IPV6_ENABLED(context)) {
|
||||
memset(&server_addr6, 0, sizeof(struct sockaddr_in6));
|
||||
server_addr6.sin6_port = htons(context->http_proxy_port);
|
||||
server_addr6.sin6_port = htons(wsi->vhost->http_proxy_port);
|
||||
} else
|
||||
#endif
|
||||
server_addr4.sin_port = htons(context->http_proxy_port);
|
||||
server_addr4.sin_port = htons(wsi->vhost->http_proxy_port);
|
||||
|
||||
} else {
|
||||
ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS);
|
||||
|
@ -151,7 +151,7 @@ lws_client_connect_2(struct lws *wsi)
|
|||
goto oom4;
|
||||
}
|
||||
|
||||
if (lws_plat_set_socket_options(context, wsi->sock)) {
|
||||
if (lws_plat_set_socket_options(wsi->vhost, wsi->sock)) {
|
||||
lwsl_err("Failed to set wsi socket options\n");
|
||||
compatible_close(wsi->sock);
|
||||
goto oom4;
|
||||
|
@ -176,7 +176,7 @@ lws_client_connect_2(struct lws *wsi)
|
|||
lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE,
|
||||
AWAITING_TIMEOUT);
|
||||
|
||||
n = lws_socket_bind(context, wsi->sock, 0, context->iface);
|
||||
n = lws_socket_bind(context, wsi->sock, 0, wsi->vhost->iface);
|
||||
if (n < 0)
|
||||
goto failed;
|
||||
}
|
||||
|
@ -222,7 +222,7 @@ lws_client_connect_2(struct lws *wsi)
|
|||
|
||||
/* we are connected to server, or proxy */
|
||||
|
||||
if (context->http_proxy_port) {
|
||||
if (wsi->vhost->http_proxy_port) {
|
||||
|
||||
/*
|
||||
* OK from now on we talk via the proxy, so connect to that
|
||||
|
@ -231,9 +231,9 @@ lws_client_connect_2(struct lws *wsi)
|
|||
* leaving old string/frag there but unreferenced)
|
||||
*/
|
||||
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
|
||||
context->http_proxy_address))
|
||||
wsi->vhost->http_proxy_address))
|
||||
goto failed;
|
||||
wsi->u.hdr.c_port = context->http_proxy_port;
|
||||
wsi->u.hdr.c_port = wsi->vhost->http_proxy_port;
|
||||
|
||||
n = send(wsi->sock, (char *)pt->serv_buf, plen,
|
||||
MSG_NOSIGNAL);
|
||||
|
@ -486,8 +486,11 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
|
|||
wsi->pending_timeout = NO_PENDING_TIMEOUT;
|
||||
wsi->position_in_fds_table = -1;
|
||||
wsi->u.hdr.c_port = i->port;
|
||||
wsi->vhost = i->vhost;
|
||||
if (!wsi->vhost)
|
||||
wsi->vhost = i->context->vhost_list;
|
||||
|
||||
wsi->protocol = &i->context->protocols[0];
|
||||
wsi->protocol = &wsi->vhost->protocols[0];
|
||||
if (wsi && !wsi->user_space && i->userdata) {
|
||||
wsi->user_space_externally_allocated = 1;
|
||||
wsi->user_space = i->userdata;
|
||||
|
|
22
lib/client.c
22
lib/client.c
|
@ -154,7 +154,7 @@ lws_client_socket_service(struct lws_context *context, struct lws *wsi,
|
|||
_WSI_TOKEN_CLIENT_HOST);
|
||||
#endif
|
||||
|
||||
wsi->ssl = SSL_new(context->ssl_client_ctx);
|
||||
wsi->ssl = SSL_new(wsi->vhost->ssl_client_ctx);
|
||||
#ifndef USE_WOLFSSL
|
||||
SSL_set_mode(wsi->ssl,
|
||||
SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
||||
|
@ -541,12 +541,12 @@ int
|
|||
lws_client_interpret_server_handshake(struct lws *wsi)
|
||||
{
|
||||
int n, len, okay = 0, isErrorCodeReceived = 0, port = 0, ssl = 0;
|
||||
struct lws_context *context = wsi->context;
|
||||
int close_reason = LWS_CLOSE_STATUS_PROTOCOL_ERR;
|
||||
const char *pc, *prot, *ads = NULL, *path;
|
||||
struct allocated_headers *ah;
|
||||
char *p;
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
struct lws_context *context = wsi->context;
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
char *sb = (char *)&pt->serv_buf[0];
|
||||
const struct lws_ext_options *opts;
|
||||
|
@ -739,7 +739,7 @@ lws_client_interpret_server_handshake(struct lws *wsi)
|
|||
* no protocol name to work from,
|
||||
* default to first protocol
|
||||
*/
|
||||
wsi->protocol = &context->protocols[0];
|
||||
wsi->protocol = &wsi->vhost->protocols[0];
|
||||
goto check_extensions;
|
||||
}
|
||||
|
||||
|
@ -768,9 +768,9 @@ lws_client_interpret_server_handshake(struct lws *wsi)
|
|||
*/
|
||||
n = 0;
|
||||
wsi->protocol = NULL;
|
||||
while (context->protocols[n].callback && !wsi->protocol) {
|
||||
if (strcmp(p, context->protocols[n].name) == 0) {
|
||||
wsi->protocol = &context->protocols[n];
|
||||
while (wsi->vhost->protocols[n].callback && !wsi->protocol) {
|
||||
if (strcmp(p, wsi->vhost->protocols[n].name) == 0) {
|
||||
wsi->protocol = &wsi->vhost->protocols[n];
|
||||
break;
|
||||
}
|
||||
n++;
|
||||
|
@ -838,7 +838,7 @@ check_extensions:
|
|||
lwsl_notice("checking client ext %s\n", ext_name);
|
||||
|
||||
n = 0;
|
||||
ext = lws_get_context(wsi)->extensions;
|
||||
ext = wsi->vhost->extensions;
|
||||
while (ext && ext->callback) {
|
||||
if (strcmp(ext_name, ext->name)) {
|
||||
ext++;
|
||||
|
@ -987,7 +987,7 @@ check_accept:
|
|||
* inform all extensions, not just active ones since they
|
||||
* already know
|
||||
*/
|
||||
ext = context->extensions;
|
||||
ext = wsi->vhost->extensions;
|
||||
|
||||
while (ext && ext->callback) {
|
||||
v = NULL;
|
||||
|
@ -1103,7 +1103,7 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
|
|||
/* tell the server what extensions we could support */
|
||||
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
ext = context->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,
|
||||
|
@ -1113,7 +1113,7 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
|
|||
ext++;
|
||||
continue;
|
||||
}
|
||||
n = context->protocols[0].callback(wsi,
|
||||
n = wsi->vhost->protocols[0].callback(wsi,
|
||||
LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED,
|
||||
wsi->user_space, (char *)ext->name, 0);
|
||||
|
||||
|
@ -1162,7 +1162,7 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
|
|||
|
||||
/* give userland a chance to append, eg, cookies */
|
||||
|
||||
context->protocols[0].callback(wsi, LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
|
||||
wsi->vhost->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");
|
||||
|
|
242
lib/context.c
242
lib/context.c
|
@ -40,6 +40,160 @@ lws_get_library_version(void)
|
|||
return library_version;
|
||||
}
|
||||
|
||||
static const char * const mount_protocols[] = {
|
||||
"http://",
|
||||
"https://",
|
||||
"file://",
|
||||
"cgi://"
|
||||
};
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_write_http_mount(struct lws_http_mount *next, struct lws_http_mount **res,
|
||||
void *store, const char *mountpoint, const char *origin,
|
||||
const char *def)
|
||||
{
|
||||
struct lws_http_mount *m;
|
||||
void *orig = store;
|
||||
unsigned long l = (unsigned long)store;
|
||||
int n;
|
||||
|
||||
if (l & 15)
|
||||
l += 16 - (l & 15);
|
||||
|
||||
store = (void *)l;
|
||||
m = (struct lws_http_mount *)store;
|
||||
*res = m;
|
||||
|
||||
m->def = def;
|
||||
m->mountpoint = mountpoint;
|
||||
m->mountpoint_len = (unsigned char)strlen(mountpoint);
|
||||
m->mount_next = NULL;
|
||||
if (next)
|
||||
next->mount_next = m;
|
||||
for (n = 0; n < ARRAY_SIZE(mount_protocols); n++)
|
||||
if (!strncmp(origin, mount_protocols[n],
|
||||
strlen(mount_protocols[n]))) {
|
||||
m->origin_protocol = n;
|
||||
m->origin = origin + strlen(mount_protocols[n]);
|
||||
break;
|
||||
}
|
||||
|
||||
if (n == ARRAY_SIZE(mount_protocols)) {
|
||||
lwsl_err("unsupported protocol://\n");
|
||||
return 0; /* ie, fail */
|
||||
}
|
||||
|
||||
return ((char *)store + sizeof(*m)) - (char *)orig;
|
||||
}
|
||||
|
||||
LWS_VISIBLE struct lws_vhost *
|
||||
lws_create_vhost(struct lws_context *context,
|
||||
struct lws_context_creation_info *info,
|
||||
struct lws_http_mount *mounts)
|
||||
{
|
||||
struct lws_vhost *vh = lws_zalloc(sizeof(*vh)),
|
||||
**vh1 = &context->vhost_list;
|
||||
struct lws wsi;
|
||||
char *p;
|
||||
int n;
|
||||
|
||||
if (!vh)
|
||||
return NULL;
|
||||
|
||||
vh->context = context;
|
||||
if (!info->vhost_name)
|
||||
vh->name = "default";
|
||||
else
|
||||
vh->name = info->vhost_name;
|
||||
|
||||
vh->iface = info->iface;
|
||||
vh->protocols = info->protocols;
|
||||
for (vh->count_protocols = 0;
|
||||
info->protocols[vh->count_protocols].callback;
|
||||
vh->count_protocols++)
|
||||
;
|
||||
|
||||
vh->mount_list = mounts;
|
||||
|
||||
lwsl_notice("Creating Vhost '%s' port %d, %d protocols\n",
|
||||
vh->name, info->port, vh->count_protocols);
|
||||
|
||||
while (mounts) {
|
||||
lwsl_notice(" mounting %s%s to %s\n",
|
||||
mount_protocols[mounts->origin_protocol],
|
||||
mounts->origin, mounts->mountpoint);
|
||||
mounts = mounts->mount_next;
|
||||
}
|
||||
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
vh->extensions = info->extensions;
|
||||
#endif
|
||||
|
||||
vh->listen_port = info->port;
|
||||
vh->http_proxy_port = 0;
|
||||
vh->http_proxy_address[0] = '\0';
|
||||
|
||||
/* 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)
|
||||
vh->http_proxy_port = info->http_proxy_port;
|
||||
lws_set_proxy(vh, info->http_proxy_address);
|
||||
} else {
|
||||
#ifdef LWS_HAVE_GETENV
|
||||
p = getenv("http_proxy");
|
||||
if (p)
|
||||
lws_set_proxy(vh, p);
|
||||
#endif
|
||||
}
|
||||
|
||||
memset(&wsi, 0, sizeof(wsi));
|
||||
wsi.context = context;
|
||||
wsi.vhost = vh;
|
||||
|
||||
/* initialize supported protocols */
|
||||
|
||||
for (n = 0; n < vh->count_protocols; n++)
|
||||
/*
|
||||
* 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 & vh ptrs
|
||||
* so lws_get_context(wsi) can work in the callback.
|
||||
*/
|
||||
info->protocols[n].callback(&wsi,
|
||||
LWS_CALLBACK_PROTOCOL_INIT, NULL, NULL, 0);
|
||||
|
||||
vh->ka_time = info->ka_time;
|
||||
vh->ka_interval = info->ka_interval;
|
||||
vh->ka_probes = info->ka_probes;
|
||||
|
||||
if (lws_context_init_server_ssl(info, vh))
|
||||
goto bail;
|
||||
|
||||
if (lws_context_init_client_ssl(info, vh))
|
||||
goto bail;
|
||||
|
||||
if (lws_context_init_server(info, vh))
|
||||
goto bail;
|
||||
|
||||
while (1) {
|
||||
if (!(*vh1)) {
|
||||
*vh1 = vh;
|
||||
break;
|
||||
}
|
||||
vh1 = &(*vh1)->vhost_next;
|
||||
};
|
||||
|
||||
return vh;
|
||||
|
||||
bail:
|
||||
lws_free(vh);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* lws_create_context() - Create the websocket handler
|
||||
* @info: pointer to struct with parameters
|
||||
|
@ -77,7 +231,6 @@ lws_create_context(struct lws_context_creation_info *info)
|
|||
#ifndef LWS_NO_DAEMONIZE
|
||||
int pid_daemon = get_daemonize_pid();
|
||||
#endif
|
||||
char *p;
|
||||
int n, m;
|
||||
|
||||
lwsl_notice("Initial logging level %d\n", log_level);
|
||||
|
@ -92,6 +245,7 @@ lws_create_context(struct lws_context_creation_info *info)
|
|||
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);
|
||||
|
@ -125,16 +279,10 @@ lws_create_context(struct lws_context_creation_info *info)
|
|||
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;
|
||||
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
|
||||
|
@ -253,6 +401,18 @@ lws_create_context(struct lws_context_creation_info *info)
|
|||
if (lws_plat_init(context, info))
|
||||
goto bail;
|
||||
|
||||
lws_context_init_ssl_library(info);
|
||||
|
||||
/*
|
||||
* 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, NULL)) {
|
||||
lwsl_err("Failed to create default vhost\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lws_context_init_extensions(info, context);
|
||||
|
||||
context->user_space = info->user;
|
||||
|
@ -263,51 +423,16 @@ lws_create_context(struct lws_context_creation_info *info)
|
|||
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;
|
||||
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
|
||||
*/
|
||||
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);
|
||||
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
|
||||
|
@ -342,6 +467,7 @@ lws_context_destroy(struct lws_context *context)
|
|||
{
|
||||
const struct lws_protocols *protocol = NULL;
|
||||
struct lws_context_per_thread *pt;
|
||||
struct lws_vhost *vh;
|
||||
struct lws wsi;
|
||||
int n, m;
|
||||
|
||||
|
@ -390,13 +516,18 @@ lws_context_destroy(struct lws_context *context)
|
|||
* 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++;
|
||||
}
|
||||
vh = context->vhost_list;
|
||||
while (vh) {
|
||||
protocol = vh->protocols;
|
||||
if (protocol)
|
||||
while (protocol->callback) {
|
||||
protocol->callback(&wsi, LWS_CALLBACK_PROTOCOL_DESTROY,
|
||||
NULL, NULL, 0);
|
||||
protocol++;
|
||||
}
|
||||
lws_ssl_SSL_CTX_destroy(vh);
|
||||
vh = vh->vhost_next;
|
||||
}
|
||||
|
||||
for (n = 0; n < context->count_threads; n++) {
|
||||
pt = &context->pt[n];
|
||||
|
@ -412,6 +543,7 @@ lws_context_destroy(struct lws_context *context)
|
|||
}
|
||||
lws_plat_context_early_destroy(context);
|
||||
lws_ssl_context_destroy(context);
|
||||
|
||||
if (context->pt[0].fds)
|
||||
lws_free_set_NULL(context->pt[0].fds);
|
||||
|
||||
|
|
|
@ -275,7 +275,7 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
|
|||
if (deflateInit2(&priv->tx, priv->args[PMD_COMP_LEVEL],
|
||||
Z_DEFLATED,
|
||||
-priv->args[PMD_CLIENT_MAX_WINDOW_BITS +
|
||||
!wsi->context->listen_port],
|
||||
!wsi->vhost->listen_port],
|
||||
priv->args[PMD_MEM_LEVEL],
|
||||
Z_DEFAULT_STRATEGY) != Z_OK) {
|
||||
lwsl_ext("inflateInit2 failed\n");
|
||||
|
|
|
@ -6,7 +6,6 @@ LWS_VISIBLE void
|
|||
lws_context_init_extensions(struct lws_context_creation_info *info,
|
||||
struct lws_context *context)
|
||||
{
|
||||
context->extensions = info->extensions;
|
||||
lwsl_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n", LWS_MAX_EXTENSIONS_ACTIVE);
|
||||
}
|
||||
|
||||
|
@ -185,7 +184,12 @@ int lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi,
|
|||
int reason, void *arg, int len)
|
||||
{
|
||||
int n = 0, m, handled = 0;
|
||||
const struct lws_extension *ext = context->extensions;
|
||||
const struct lws_extension *ext;
|
||||
|
||||
if (!wsi || !wsi->vhost)
|
||||
return 0;
|
||||
|
||||
ext = wsi->vhost->extensions;
|
||||
|
||||
while (ext && ext->callback && !handled) {
|
||||
m = ext->callback(context, ext, wsi, reason,
|
||||
|
|
13
lib/http2.c
13
lib/http2.c
|
@ -52,10 +52,10 @@ lws_http2_wsi_from_id(struct lws *wsi, unsigned int sid)
|
|||
}
|
||||
|
||||
struct lws *
|
||||
lws_create_server_child_wsi(struct lws_context *context, struct lws *parent_wsi,
|
||||
lws_create_server_child_wsi(struct lws_vhost *vhost, struct lws *parent_wsi,
|
||||
unsigned int sid)
|
||||
{
|
||||
struct lws *wsi = lws_create_new_server_wsi(context);
|
||||
struct lws *wsi = lws_create_new_server_wsi(vhost);
|
||||
|
||||
if (!wsi)
|
||||
return NULL;
|
||||
|
@ -82,7 +82,7 @@ lws_create_server_child_wsi(struct lws_context *context, struct lws *parent_wsi,
|
|||
wsi->state = LWSS_HTTP2_ESTABLISHED;
|
||||
wsi->mode = parent_wsi->mode;
|
||||
|
||||
wsi->protocol = &context->protocols[0];
|
||||
wsi->protocol = &vhost->protocols[0];
|
||||
lws_ensure_user_space(wsi);
|
||||
|
||||
lwsl_info("%s: %p new child %p, sid %d, user_space=%p\n", __func__,
|
||||
|
@ -198,7 +198,6 @@ static const char * https_client_preface =
|
|||
int
|
||||
lws_http2_parser(struct lws *wsi, unsigned char c)
|
||||
{
|
||||
struct lws_context *context = wsi->context;
|
||||
struct lws *swsi;
|
||||
int n;
|
||||
|
||||
|
@ -378,7 +377,8 @@ lws_http2_parser(struct lws *wsi, unsigned char c)
|
|||
if (!wsi->u.http2.stream_id)
|
||||
return 1;
|
||||
if (!wsi->u.http2.stream_wsi)
|
||||
wsi->u.http2.stream_wsi = lws_create_server_child_wsi(context, wsi, wsi->u.http2.stream_id);
|
||||
wsi->u.http2.stream_wsi =
|
||||
lws_create_server_child_wsi(wsi->vhost, wsi, wsi->u.http2.stream_id);
|
||||
|
||||
/* END_STREAM means after servicing this, close the stream */
|
||||
wsi->u.http2.END_STREAM = !!(wsi->u.http2.flags & LWS_HTTP2_FLAG_END_STREAM);
|
||||
|
@ -474,7 +474,8 @@ int lws_http2_do_pps_send(struct lws_context *context, struct lws *wsi)
|
|||
*/
|
||||
lwsl_info("%s: setting up sid 1\n", __func__);
|
||||
|
||||
swsi = wsi->u.http2.stream_wsi = lws_create_server_child_wsi(context, wsi, 1);
|
||||
swsi = wsi->u.http2.stream_wsi =
|
||||
lws_create_server_child_wsi(wsi->vhost, wsi, 1);
|
||||
/* pass on the initial headers to SID 1 */
|
||||
swsi->u.http.ah = wsi->u.http.ah;
|
||||
wsi->u.http.ah = NULL;
|
||||
|
|
12
lib/libev.c
12
lib/libev.c
|
@ -78,6 +78,7 @@ lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi)
|
|||
{
|
||||
struct ev_signal *w_sigint = &context->pt[tsi].w_sigint.ev_watcher;
|
||||
struct ev_io *w_accept = &context->pt[tsi].w_accept.ev_watcher;
|
||||
struct lws_vhost *vh = context->vhost_list;
|
||||
const char * backend_name;
|
||||
int status = 0;
|
||||
int backend;
|
||||
|
@ -90,10 +91,17 @@ lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi)
|
|||
context->pt[tsi].io_loop_ev = loop;
|
||||
|
||||
/*
|
||||
* Initialize the accept w_accept with the listening socket
|
||||
* Initialize the accept w_accept with all the listening sockets
|
||||
* and register a callback for read operations
|
||||
*/
|
||||
ev_io_init(w_accept, lws_accept_cb, context->pt[tsi].lserv_fd, EV_READ);
|
||||
while (vh) {
|
||||
if (vh->lserv_wsi) {
|
||||
vh->lserv_wsi->w_read.context = context;
|
||||
ev_io_init(w_accept, lws_accept_cb, vh->lserv_wsi->sock,
|
||||
EV_READ);
|
||||
}
|
||||
vh = vh->vhost_next;
|
||||
}
|
||||
ev_io_start(context->pt[tsi].io_loop_ev, w_accept);
|
||||
|
||||
/* Register the signal watcher unless the user says not to */
|
||||
|
|
23
lib/libuv.c
23
lib/libuv.c
|
@ -102,7 +102,7 @@ LWS_VISIBLE int
|
|||
lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[tsi];
|
||||
struct lws *wsi = wsi_from_fd(context, pt->lserv_fd);
|
||||
struct lws_vhost *vh = context->vhost_list;
|
||||
int status = 0, n;
|
||||
|
||||
if (!loop) {
|
||||
|
@ -119,23 +119,28 @@ lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi)
|
|||
for (n = 0; n < ARRAY_SIZE(sigs); n++) {
|
||||
uv_signal_init(loop, &pt->signals[n]);
|
||||
pt->signals[n].data = pt->context;
|
||||
uv_signal_start(&pt->signals[n], context->lws_uv_sigint_cb, sigs[n]);
|
||||
uv_signal_start(&pt->signals[n],
|
||||
context->lws_uv_sigint_cb, sigs[n]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the accept wsi read watcher with the listening socket
|
||||
* Initialize the accept wsi read watcher with all the listening sockets
|
||||
* and register a callback for read operations
|
||||
*
|
||||
* We have to do it here because the uv loop(s) are not
|
||||
* initialized until after context creation.
|
||||
*/
|
||||
if (wsi) {
|
||||
wsi->w_read.context = context;
|
||||
uv_poll_init(pt->io_loop_uv, &wsi->w_read.uv_watcher,
|
||||
pt->lserv_fd);
|
||||
uv_poll_start(&wsi->w_read.uv_watcher, UV_READABLE,
|
||||
lws_io_cb);
|
||||
while (vh) {
|
||||
if (vh->lserv_wsi) {
|
||||
vh->lserv_wsi->w_read.context = context;
|
||||
uv_poll_init(pt->io_loop_uv,
|
||||
&vh->lserv_wsi->w_read.uv_watcher,
|
||||
vh->lserv_wsi->sock);
|
||||
uv_poll_start(&vh->lserv_wsi->w_read.uv_watcher,
|
||||
UV_READABLE, lws_io_cb);
|
||||
}
|
||||
vh = vh->vhost_next;
|
||||
}
|
||||
|
||||
uv_timer_init(pt->io_loop_uv, &pt->uv_timeout_watcher);
|
||||
|
|
|
@ -194,8 +194,8 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
|
|||
wsi->u.http.fd != LWS_INVALID_FILE) {
|
||||
lws_plat_file_close(wsi, wsi->u.http.fd);
|
||||
wsi->u.http.fd = LWS_INVALID_FILE;
|
||||
context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
|
||||
wsi->user_space, NULL, 0);
|
||||
wsi->vhost->protocols[0].callback(wsi,
|
||||
LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0);
|
||||
}
|
||||
if (wsi->socket_is_permanently_unusable ||
|
||||
reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY ||
|
||||
|
@ -234,10 +234,10 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
|
|||
goto just_kill_connection;
|
||||
|
||||
if (wsi->mode == LWSCM_HTTP_SERVING)
|
||||
context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
|
||||
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
|
||||
wsi->user_space, NULL, 0);
|
||||
if (wsi->mode == LWSCM_HTTP_CLIENT)
|
||||
context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_CLIENT_HTTP,
|
||||
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_CLIENT_HTTP,
|
||||
wsi->user_space, NULL, 0);
|
||||
|
||||
/*
|
||||
|
@ -468,12 +468,12 @@ just_kill_connection:
|
|||
wsi->user_space, NULL, 0);
|
||||
} else if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED) {
|
||||
lwsl_debug("calling back CLOSED_HTTP\n");
|
||||
context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
|
||||
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
|
||||
wsi->user_space, NULL, 0 );
|
||||
} else if (wsi->mode == LWSCM_WSCL_WAITING_SERVER_REPLY ||
|
||||
wsi->mode == LWSCM_WSCL_WAITING_CONNECT) {
|
||||
lwsl_debug("Connection closed before server reply\n");
|
||||
context->protocols[0].callback(wsi,
|
||||
wsi->vhost->protocols[0].callback(wsi,
|
||||
LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
|
||||
wsi->user_space, NULL, 0);
|
||||
} else
|
||||
|
@ -525,7 +525,7 @@ lws_close_free_wsi_final(struct lws *wsi)
|
|||
}
|
||||
|
||||
/* outermost destroy notification for wsi (user_space still intact) */
|
||||
wsi->context->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY,
|
||||
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY,
|
||||
wsi->user_space, NULL, 0);
|
||||
|
||||
lws_free_wsi(wsi);
|
||||
|
@ -903,7 +903,7 @@ int user_callback_handle_rxflow(lws_callback_function callback_function,
|
|||
*/
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_set_proxy(struct lws_context *context, const char *proxy)
|
||||
lws_set_proxy(struct lws_vhost *vhost, const char *proxy)
|
||||
{
|
||||
char *p;
|
||||
char authstring[96];
|
||||
|
@ -920,35 +920,35 @@ lws_set_proxy(struct lws_context *context, const char *proxy)
|
|||
strncpy(authstring, proxy, p - proxy);
|
||||
// null termination not needed on input
|
||||
if (lws_b64_encode_string(authstring, (p - proxy),
|
||||
context->proxy_basic_auth_token,
|
||||
sizeof context->proxy_basic_auth_token) < 0)
|
||||
vhost->proxy_basic_auth_token,
|
||||
sizeof vhost->proxy_basic_auth_token) < 0)
|
||||
goto auth_too_long;
|
||||
|
||||
lwsl_notice(" Proxy auth in use\n");
|
||||
|
||||
proxy = p + 1;
|
||||
} else
|
||||
context->proxy_basic_auth_token[0] = '\0';
|
||||
vhost->proxy_basic_auth_token[0] = '\0';
|
||||
|
||||
strncpy(context->http_proxy_address, proxy,
|
||||
sizeof(context->http_proxy_address) - 1);
|
||||
context->http_proxy_address[
|
||||
sizeof(context->http_proxy_address) - 1] = '\0';
|
||||
strncpy(vhost->http_proxy_address, proxy,
|
||||
sizeof(vhost->http_proxy_address) - 1);
|
||||
vhost->http_proxy_address[
|
||||
sizeof(vhost->http_proxy_address) - 1] = '\0';
|
||||
|
||||
p = strchr(context->http_proxy_address, ':');
|
||||
if (!p && !context->http_proxy_port) {
|
||||
p = strchr(vhost->http_proxy_address, ':');
|
||||
if (!p && !vhost->http_proxy_port) {
|
||||
lwsl_err("http_proxy needs to be ads:port\n");
|
||||
|
||||
return -1;
|
||||
} else {
|
||||
if (p) {
|
||||
*p = '\0';
|
||||
context->http_proxy_port = atoi(p + 1);
|
||||
vhost->http_proxy_port = atoi(p + 1);
|
||||
}
|
||||
}
|
||||
|
||||
lwsl_notice(" Proxy %s:%u\n", context->http_proxy_address,
|
||||
context->http_proxy_port);
|
||||
lwsl_notice(" Proxy %s:%u\n", vhost->http_proxy_address,
|
||||
vhost->http_proxy_port);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -1469,8 +1469,8 @@ lws_socket_bind(struct lws_context *context, int sockfd, int port,
|
|||
|
||||
n = bind(sockfd, v, n);
|
||||
if (n < 0) {
|
||||
lwsl_err("ERROR on binding to port %d (%d %d)\n",
|
||||
port, n, LWS_ERRNO);
|
||||
lwsl_err("ERROR on binding fd %d to port %d (%d %d)\n",
|
||||
sockfd, port, n, LWS_ERRNO);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1512,6 +1512,21 @@ lws_urlencode(const char *in, int inlen, char *out, int outlen)
|
|||
return out - start;
|
||||
}
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_finalize_startup(struct lws_context *context)
|
||||
{
|
||||
struct lws_context_creation_info info;
|
||||
|
||||
info.uid = context->uid;
|
||||
info.gid = context->gid;
|
||||
|
||||
if (lws_check_opt(context->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
|
||||
lws_plat_drop_app_privileges(&info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_is_cgi(struct lws *wsi) {
|
||||
#ifdef LWS_WITH_CGI
|
||||
|
@ -1555,10 +1570,10 @@ lws_create_basic_wsi(struct lws_context *context, int tsi)
|
|||
/*
|
||||
* these can only be set once the protocol is known
|
||||
* we set an unestablished connection's protocol pointer
|
||||
* to the start of the supported list, so it can look
|
||||
* to the start of the defauly vhost supported list, so it can look
|
||||
* for matching ones during the handshake
|
||||
*/
|
||||
new_wsi->protocol = context->protocols;
|
||||
new_wsi->protocol = context->vhost_list->protocols;
|
||||
new_wsi->user_space = NULL;
|
||||
new_wsi->ietf_spec_revision = 0;
|
||||
new_wsi->sock = LWS_SOCK_INVALID;
|
||||
|
|
|
@ -307,6 +307,7 @@ enum lws_context_options {
|
|||
(1 << 3) |
|
||||
(1 << 12),
|
||||
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT = (1 << 12),
|
||||
LWS_SERVER_OPTION_EXPLICIT_VHOSTS = (1 << 13),
|
||||
|
||||
/****** add new things just above ---^ ******/
|
||||
};
|
||||
|
@ -1292,6 +1293,13 @@ extern int lws_extension_callback_pm_deflate(
|
|||
/**
|
||||
* struct lws_context_creation_info - parameters to create context with
|
||||
*
|
||||
* This is also used to create vhosts.... if LWS_SERVER_OPTION_EXPLICIT_VHOSTS
|
||||
* is not given, then for backwards compatibility one vhost is created at
|
||||
* context-creation time using the info from this struct.
|
||||
*
|
||||
* If LWS_SERVER_OPTION_EXPLICIT_VHOSTS is given, then no vhosts are created
|
||||
* at the same time as the context, they are expected to be created afterwards.
|
||||
*
|
||||
* @port: Port to listen on... you can use CONTEXT_PORT_NO_LISTEN 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
|
||||
|
@ -1359,22 +1367,22 @@ extern int lws_extension_callback_pm_deflate(
|
|||
*/
|
||||
|
||||
struct lws_context_creation_info {
|
||||
int port;
|
||||
const char *iface;
|
||||
const struct lws_protocols *protocols;
|
||||
const struct lws_extension *extensions;
|
||||
int port; /* VH */
|
||||
const char *iface; /* VH */
|
||||
const struct lws_protocols *protocols; /* VH */
|
||||
const struct lws_extension *extensions; /* VH */
|
||||
const struct lws_token_limits *token_limits;
|
||||
const char *ssl_private_key_password;
|
||||
const char *ssl_cert_filepath;
|
||||
const char *ssl_private_key_filepath;
|
||||
const char *ssl_ca_filepath;
|
||||
const char *ssl_cipher_list;
|
||||
const char *http_proxy_address;
|
||||
unsigned int http_proxy_port;
|
||||
int gid;
|
||||
int uid;
|
||||
unsigned int options;
|
||||
void *user;
|
||||
const char *ssl_private_key_password; /* VH */
|
||||
const char *ssl_cert_filepath; /* VH */
|
||||
const char *ssl_private_key_filepath; /* VH */
|
||||
const char *ssl_ca_filepath; /* VH */
|
||||
const char *ssl_cipher_list; /* VH */
|
||||
const char *http_proxy_address; /* VH */
|
||||
unsigned int http_proxy_port; /* VH */
|
||||
int gid; /* context */
|
||||
int uid; /* context */
|
||||
unsigned int options; /* context */
|
||||
void *user; /* context */
|
||||
int ka_time;
|
||||
int ka_probes;
|
||||
int ka_interval;
|
||||
|
@ -1387,10 +1395,11 @@ struct lws_context_creation_info {
|
|||
short max_http_header_data;
|
||||
short max_http_header_pool;
|
||||
|
||||
unsigned int count_threads;
|
||||
unsigned int fd_limit_per_thread;
|
||||
unsigned int timeout_secs;
|
||||
const char *ecdh_curve;
|
||||
unsigned int count_threads; /* context */
|
||||
unsigned int fd_limit_per_thread; /* context */
|
||||
unsigned int timeout_secs; /* VH */
|
||||
const char *ecdh_curve; /* VH */
|
||||
const char *vhost_name; /* VH */
|
||||
|
||||
/* Add new things just above here ---^
|
||||
* This is part of the ABI, don't needlessly break compatibility
|
||||
|
@ -1426,6 +1435,7 @@ struct lws_context_creation_info {
|
|||
* @uri_replace_from: if non-NULL, when this string is found in URIs in
|
||||
* text/html content-encoding, it's replaced with @uri_replace_to
|
||||
* @uri_replace_to: see above
|
||||
* @vhost: vhost to bind to (used to determine related SSL_CTX)
|
||||
*/
|
||||
|
||||
struct lws_client_connect_info {
|
||||
|
@ -1444,6 +1454,7 @@ struct lws_client_connect_info {
|
|||
struct lws *parent_wsi;
|
||||
const char *uri_replace_from;
|
||||
const char *uri_replace_to;
|
||||
struct lws_vhost *vhost;
|
||||
|
||||
/* Add new things just above here ---^
|
||||
* This is part of the ABI, don't needlessly break compatibility
|
||||
|
@ -1456,6 +1467,13 @@ struct lws_client_connect_info {
|
|||
void *_unused[4];
|
||||
};
|
||||
|
||||
struct lws_http_mount;
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_write_http_mount(struct lws_http_mount *next, struct lws_http_mount **res,
|
||||
void *store, const char *mountpoint, const char *origin,
|
||||
const char *def);
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_set_log_level(int level,
|
||||
void (*log_emit_function)(int level, const char *line));
|
||||
|
@ -1466,8 +1484,18 @@ lwsl_emit_syslog(int level, const char *line);
|
|||
LWS_VISIBLE LWS_EXTERN struct lws_context *
|
||||
lws_create_context(struct lws_context_creation_info *info);
|
||||
|
||||
struct lws_vhost;
|
||||
|
||||
LWS_VISIBLE struct lws_vhost *
|
||||
lws_create_vhost(struct lws_context *context,
|
||||
struct lws_context_creation_info *info,
|
||||
struct lws_http_mount *mounts);
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_set_proxy(struct lws_context *context, const char *proxy);
|
||||
lws_finalize_startup(struct lws_context *context);
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_set_proxy(struct lws_vhost *vhost, const char *proxy);
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_context_destroy(struct lws_context *context);
|
||||
|
|
|
@ -127,7 +127,7 @@ lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
|
||||
/* stay dead once we are dead */
|
||||
|
||||
if (!context)
|
||||
if (!context || !context->vhost_list)
|
||||
return 1;
|
||||
|
||||
lws_libev_run(context, tsi);
|
||||
|
@ -139,7 +139,7 @@ lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
memset(&_lws, 0, sizeof(_lws));
|
||||
_lws.context = context;
|
||||
|
||||
context->service_tid_detected = context->protocols[0].callback(
|
||||
context->service_tid_detected = context->vhost_list->protocols[0].callback(
|
||||
&_lws, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
|
||||
}
|
||||
context->service_tid = context->service_tid_detected;
|
||||
|
@ -200,7 +200,7 @@ lws_plat_service(struct lws_context *context, int timeout_ms)
|
|||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_set_socket_options(struct lws_context *context, int fd)
|
||||
lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
|
||||
{
|
||||
int optval = 1;
|
||||
socklen_t optlen = sizeof(optval);
|
||||
|
@ -212,7 +212,7 @@ lws_plat_set_socket_options(struct lws_context *context, int fd)
|
|||
struct protoent *tcp_proto;
|
||||
#endif
|
||||
|
||||
if (context->ka_time) {
|
||||
if (vhost->ka_time) {
|
||||
/* enable keepalive on this socket */
|
||||
optval = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
|
||||
|
@ -230,17 +230,17 @@ lws_plat_set_socket_options(struct lws_context *context, int fd)
|
|||
*/
|
||||
#else
|
||||
/* set the keepalive conditions we want on it too */
|
||||
optval = context->ka_time;
|
||||
optval = vhost->ka_time;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE,
|
||||
(const void *)&optval, optlen) < 0)
|
||||
return 1;
|
||||
|
||||
optval = context->ka_interval;
|
||||
optval = vhost->ka_interval;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL,
|
||||
(const void *)&optval, optlen) < 0)
|
||||
return 1;
|
||||
|
||||
optval = context->ka_probes;
|
||||
optval = vhost->ka_probes;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT,
|
||||
(const void *)&optval, optlen) < 0)
|
||||
return 1;
|
||||
|
@ -283,7 +283,7 @@ lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
|
|||
if (setuid(info->uid))
|
||||
lwsl_warn("setuid: %s\n", strerror(LWS_ERRNO));
|
||||
else
|
||||
lwsl_notice(" Set privs to user '%s'\n", p->pw_name);
|
||||
lwsl_notice("Set privs to user '%s'\n", p->pw_name);
|
||||
} else
|
||||
lwsl_warn("getpwuid: unable to find uid %d", info->uid);
|
||||
}
|
||||
|
|
|
@ -171,28 +171,30 @@ lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
memset(&_lws, 0, sizeof(_lws));
|
||||
_lws.context = context;
|
||||
|
||||
context->service_tid_detected = context->protocols[0].callback(
|
||||
&_lws, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
|
||||
context->service_tid_detected = context->vhost_list->
|
||||
protocols[0].callback(&_lws, LWS_CALLBACK_GET_THREAD_ID,
|
||||
NULL, NULL, 0);
|
||||
}
|
||||
context->service_tid = context->service_tid_detected;
|
||||
|
||||
for (i = 0; i < pt->fds_count; ++i) {
|
||||
pfd = &pt->fds[i];
|
||||
if (pfd->fd == pt->lserv_fd)
|
||||
|
||||
if (!(pfd->events & LWS_POLLOUT))
|
||||
continue;
|
||||
|
||||
if (pfd->events & LWS_POLLOUT) {
|
||||
wsi = wsi_from_fd(context, pfd->fd);
|
||||
if (!wsi || wsi->sock_send_blocking)
|
||||
continue;
|
||||
pfd->revents = LWS_POLLOUT;
|
||||
n = lws_service_fd(context, pfd);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
/* if something closed, retry this slot */
|
||||
if (n)
|
||||
i--;
|
||||
}
|
||||
wsi = wsi_from_fd(context, pfd->fd);
|
||||
if (wsi->listener)
|
||||
continue;
|
||||
if (!wsi || wsi->sock_send_blocking)
|
||||
continue;
|
||||
pfd->revents = LWS_POLLOUT;
|
||||
n = lws_service_fd(context, pfd);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
/* if something closed, retry this slot */
|
||||
if (n)
|
||||
i--;
|
||||
}
|
||||
|
||||
/* if we know something needs service already, don't wait in poll */
|
||||
|
@ -264,7 +266,7 @@ lws_plat_service(struct lws_context *context, int timeout_ms)
|
|||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_set_socket_options(struct lws_context *context, lws_sockfd_type fd)
|
||||
lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd)
|
||||
{
|
||||
int optval = 1;
|
||||
int optlen = sizeof(optval);
|
||||
|
@ -276,7 +278,7 @@ lws_plat_set_socket_options(struct lws_context *context, lws_sockfd_type fd)
|
|||
struct protoent *tcp_proto;
|
||||
#endif
|
||||
|
||||
if (context->ka_time) {
|
||||
if (vhost->ka_time) {
|
||||
/* enable keepalive on this socket */
|
||||
optval = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
|
||||
|
@ -284,8 +286,8 @@ lws_plat_set_socket_options(struct lws_context *context, lws_sockfd_type fd)
|
|||
return 1;
|
||||
|
||||
alive.onoff = TRUE;
|
||||
alive.keepalivetime = context->ka_time;
|
||||
alive.keepaliveinterval = context->ka_interval;
|
||||
alive.keepalivetime = vhost->ka_time;
|
||||
alive.keepaliveinterval = vhost->ka_interval;
|
||||
|
||||
if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive),
|
||||
NULL, 0, &dwBytesRet, NULL, NULL))
|
||||
|
|
24
lib/pollfd.c
24
lib/pollfd.c
|
@ -43,7 +43,7 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
|
|||
pa->prev_events = pfd->events;
|
||||
pa->events = pfd->events = (pfd->events & ~_and) | _or;
|
||||
|
||||
if (context->protocols[0].callback(wsi, LWS_CALLBACK_CHANGE_MODE_POLL_FD,
|
||||
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CHANGE_MODE_POLL_FD,
|
||||
wsi->user_space, (void *)pa, 0)) {
|
||||
ret = -1;
|
||||
goto bail;
|
||||
|
@ -86,7 +86,7 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
|
|||
|
||||
sampled_tid = context->service_tid;
|
||||
if (sampled_tid) {
|
||||
tid = context->protocols[0].callback(wsi,
|
||||
tid = wsi->vhost->protocols[0].callback(wsi,
|
||||
LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
|
||||
if (tid == -1) {
|
||||
ret = -1;
|
||||
|
@ -114,7 +114,8 @@ insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
|
|||
__func__, wsi, wsi->tsi, wsi->sock, pt->fds_count);
|
||||
|
||||
if ((unsigned int)pt->fds_count >= context->fd_limit_per_thread) {
|
||||
lwsl_err("Too many fds (%d)\n", context->max_fds);
|
||||
lwsl_err("Too many fds (%d vs %d)\n", context->max_fds,
|
||||
context->fd_limit_per_thread );
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -127,9 +128,10 @@ insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
|
|||
#endif
|
||||
|
||||
assert(wsi);
|
||||
assert(wsi->vhost);
|
||||
assert(lws_socket_is_valid(wsi->sock));
|
||||
|
||||
if (context->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
|
||||
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
|
||||
wsi->user_space, (void *) &pa, 1))
|
||||
return -1;
|
||||
|
||||
|
@ -144,7 +146,7 @@ insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
|
|||
lws_plat_insert_socket_into_fds(context, wsi);
|
||||
|
||||
/* external POLL support via protocol 0 */
|
||||
if (context->protocols[0].callback(wsi, LWS_CALLBACK_ADD_POLL_FD,
|
||||
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_ADD_POLL_FD,
|
||||
wsi->user_space, (void *) &pa, 0))
|
||||
ret = -1;
|
||||
#ifndef LWS_NO_SERVER
|
||||
|
@ -154,7 +156,7 @@ insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
|
|||
#endif
|
||||
lws_pt_unlock(pt);
|
||||
|
||||
if (context->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
|
||||
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
|
||||
wsi->user_space, (void *)&pa, 1))
|
||||
ret = -1;
|
||||
|
||||
|
@ -180,7 +182,7 @@ remove_wsi_socket_from_fds(struct lws *wsi)
|
|||
}
|
||||
#endif
|
||||
|
||||
if (context->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
|
||||
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
|
||||
wsi->user_space, (void *)&pa, 1))
|
||||
return -1;
|
||||
|
||||
|
@ -213,7 +215,7 @@ remove_wsi_socket_from_fds(struct lws *wsi)
|
|||
|
||||
/* remove also from external POLL support via protocol 0 */
|
||||
if (lws_socket_is_valid(wsi->sock))
|
||||
if (context->protocols[0].callback(wsi, LWS_CALLBACK_DEL_POLL_FD,
|
||||
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_DEL_POLL_FD,
|
||||
wsi->user_space, (void *) &pa, 0))
|
||||
ret = -1;
|
||||
#ifndef LWS_NO_SERVER
|
||||
|
@ -224,7 +226,7 @@ remove_wsi_socket_from_fds(struct lws *wsi)
|
|||
#endif
|
||||
lws_pt_unlock(pt);
|
||||
|
||||
if (context->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
|
||||
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
|
||||
wsi->user_space, (void *) &pa, 1))
|
||||
ret = -1;
|
||||
|
||||
|
@ -246,7 +248,7 @@ lws_change_pollfd(struct lws *wsi, int _and, int _or)
|
|||
if (!context)
|
||||
return 1;
|
||||
|
||||
if (context->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
|
||||
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
|
||||
wsi->user_space, (void *) &pa, 0))
|
||||
return -1;
|
||||
|
||||
|
@ -255,7 +257,7 @@ lws_change_pollfd(struct lws *wsi, int _and, int _or)
|
|||
lws_pt_lock(pt);
|
||||
ret = _lws_change_pollfd(wsi, _and, _or, &pa);
|
||||
lws_pt_unlock(pt);
|
||||
if (context->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
|
||||
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
|
||||
wsi->user_space, (void *) &pa, 0))
|
||||
ret = -1;
|
||||
|
||||
|
|
|
@ -581,7 +581,6 @@ struct lws_context_per_thread {
|
|||
struct lws_signal_watcher w_sigint;
|
||||
unsigned char ev_loop_foreign:1;
|
||||
#endif
|
||||
lws_sockfd_type lserv_fd;
|
||||
|
||||
unsigned long count_conns;
|
||||
/*
|
||||
|
@ -601,13 +600,71 @@ struct lws_context_per_thread {
|
|||
unsigned char tid;
|
||||
};
|
||||
|
||||
struct lws_http_mount {
|
||||
struct lws_http_mount *mount_next;
|
||||
const char *mountpoint; /* mountpoint in http pathspace, eg, "/" */
|
||||
const char *origin; /* path to be mounted, eg, "/var/www/warmcat.com" */
|
||||
const char *def; /* default target, eg, "index.html" */
|
||||
|
||||
unsigned char origin_protocol;
|
||||
unsigned char mountpoint_len;
|
||||
};
|
||||
|
||||
/*
|
||||
* virtual host -related context information
|
||||
* vhostwide SSL context
|
||||
* vhostwide proxy
|
||||
*
|
||||
* heirarchy:
|
||||
*
|
||||
* context -> vhost -> wsi
|
||||
*
|
||||
* incoming connection non-SSL vhost binding:
|
||||
*
|
||||
* listen socket -> wsi -> select vhost after first headers
|
||||
*
|
||||
* incoming connection SSL vhost binding:
|
||||
*
|
||||
* SSL SNI -> wsi -> bind after SSL negotiation
|
||||
*/
|
||||
|
||||
struct lws_vhost {
|
||||
char http_proxy_address[128];
|
||||
char proxy_basic_auth_token[128];
|
||||
struct lws_context *context;
|
||||
struct lws_vhost *vhost_next;
|
||||
struct lws_http_mount *mount_list;
|
||||
struct lws *lserv_wsi;
|
||||
const char *name;
|
||||
const char *iface;
|
||||
const struct lws_protocols *protocols;
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
SSL_CTX *ssl_ctx;
|
||||
SSL_CTX *ssl_client_ctx;
|
||||
#endif
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
const struct lws_extension *extensions;
|
||||
#endif
|
||||
|
||||
int listen_port;
|
||||
unsigned int http_proxy_port;
|
||||
int count_protocols;
|
||||
int ka_time;
|
||||
int ka_probes;
|
||||
int ka_interval;
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
int use_ssl;
|
||||
int allow_non_ssl_on_ssl_port;
|
||||
unsigned int user_supplied_ssl_ctx:1;
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* the rest is managed per-context, that includes
|
||||
*
|
||||
* - processwide single fd -> wsi lookup
|
||||
* - contextwide headers pool
|
||||
* - contextwide ssl context
|
||||
* - contextwide proxy
|
||||
*/
|
||||
|
||||
struct lws_context {
|
||||
|
@ -620,27 +677,16 @@ struct lws_context {
|
|||
#else
|
||||
struct lws **lws_lookup; /* fd to wsi */
|
||||
#endif
|
||||
const char *iface;
|
||||
struct lws_vhost *vhost_list;
|
||||
const struct lws_token_limits *token_limits;
|
||||
void *user_space;
|
||||
|
||||
const struct lws_protocols *protocols;
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
SSL_CTX *ssl_ctx;
|
||||
SSL_CTX *ssl_client_ctx;
|
||||
#endif
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
const struct lws_extension *extensions;
|
||||
#endif
|
||||
#if defined(LWS_USE_LIBEV)
|
||||
lws_ev_signal_cb_t * lws_ev_sigint_cb;
|
||||
#endif
|
||||
#if defined(LWS_USE_LIBUV)
|
||||
uv_signal_cb lws_uv_sigint_cb;
|
||||
#endif
|
||||
char http_proxy_address[128];
|
||||
char proxy_basic_auth_token[128];
|
||||
char canonical_hostname[128];
|
||||
#ifdef LWS_LATENCY
|
||||
unsigned long worst_latency;
|
||||
|
@ -648,16 +694,25 @@ struct lws_context {
|
|||
#endif
|
||||
|
||||
int max_fds;
|
||||
int listen_port;
|
||||
#if defined(LWS_USE_LIBEV) || defined(LWS_USE_LIBUV)
|
||||
int use_ev_sigint;
|
||||
#endif
|
||||
int started_with_parent;
|
||||
int uid, gid;
|
||||
|
||||
int fd_random;
|
||||
int lserv_mod;
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#define lws_ssl_anybody_has_buffered_read(w) \
|
||||
(w->vhost->use_ssl && \
|
||||
w->context->pt[(int)w->tsi].pending_read_list)
|
||||
#define lws_ssl_anybody_has_buffered_read_tsi(c, t) \
|
||||
(/*c->use_ssl && */ \
|
||||
c->pt[(int)t].pending_read_list)
|
||||
#else
|
||||
#define lws_ssl_anybody_has_buffered_read(ctx) (0)
|
||||
#define lws_ssl_anybody_has_buffered_read_tsi(ctx, t) (0)
|
||||
#endif
|
||||
int count_wsi_allocated;
|
||||
unsigned int http_proxy_port;
|
||||
unsigned int options;
|
||||
unsigned int fd_limit_per_thread;
|
||||
unsigned int timeout_secs;
|
||||
|
@ -671,26 +726,6 @@ struct lws_context {
|
|||
volatile int service_tid;
|
||||
int service_tid_detected;
|
||||
|
||||
int count_protocols;
|
||||
int ka_time;
|
||||
int ka_probes;
|
||||
int ka_interval;
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
int use_ssl;
|
||||
int allow_non_ssl_on_ssl_port;
|
||||
unsigned int user_supplied_ssl_ctx:1;
|
||||
#define lws_ssl_anybody_has_buffered_read(w) \
|
||||
(w->context->use_ssl && \
|
||||
w->context->pt[(int)w->tsi].pending_read_list)
|
||||
#define lws_ssl_anybody_has_buffered_read_tsi(c, t) \
|
||||
(c->use_ssl && \
|
||||
c->pt[(int)t].pending_read_list)
|
||||
#else
|
||||
#define lws_ssl_anybody_has_buffered_read(ctx) (0)
|
||||
#define lws_ssl_anybody_has_buffered_read_tsi(ctx, t) (0)
|
||||
#endif
|
||||
|
||||
short max_http_header_data;
|
||||
short max_http_header_pool;
|
||||
short count_threads;
|
||||
|
@ -699,6 +734,9 @@ struct lws_context {
|
|||
unsigned int requested_kill:1;
|
||||
};
|
||||
|
||||
#define lws_get_context_protocol(ctx, x) ctx->vhost_list->protocols[x]
|
||||
#define lws_get_vh_protocol(vh, x) vh->protocols[x]
|
||||
|
||||
LWS_EXTERN void
|
||||
lws_close_free_wsi_final(struct lws *wsi);
|
||||
LWS_EXTERN void
|
||||
|
@ -1101,6 +1139,7 @@ struct lws {
|
|||
/* pointers */
|
||||
|
||||
struct lws_context *context;
|
||||
struct lws_vhost *vhost;
|
||||
struct lws *parent; /* points to parent, if any */
|
||||
struct lws *child_list; /* points to first child */
|
||||
struct lws *sibling_list; /* subsequent children at same level */
|
||||
|
@ -1146,6 +1185,7 @@ struct lws {
|
|||
#endif
|
||||
|
||||
unsigned int hdr_parsing_completed:1;
|
||||
unsigned int listener:1;
|
||||
unsigned int user_space_externally_allocated:1;
|
||||
unsigned int socket_is_permanently_unusable:1;
|
||||
unsigned int rxflow_change_to:2;
|
||||
|
@ -1282,7 +1322,7 @@ lws_client_reset(struct lws *wsi, int ssl, const char *address, int port,
|
|||
const char *path, const char *host);
|
||||
|
||||
LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT
|
||||
lws_create_new_server_wsi(struct lws_context *context);
|
||||
lws_create_new_server_wsi(struct lws_vhost *vhost);
|
||||
|
||||
LWS_EXTERN char * LWS_WARN_UNUSED_RESULT
|
||||
lws_generate_client_handshake(struct lws *wsi, char *pkt);
|
||||
|
@ -1377,7 +1417,7 @@ void lws_http2_configure_if_upgraded(struct lws *wsi);
|
|||
#endif
|
||||
|
||||
LWS_EXTERN int
|
||||
lws_plat_set_socket_options(struct lws_context *context, lws_sockfd_type fd);
|
||||
lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd);
|
||||
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
lws_header_table_attach(struct lws *wsi, int autoservice);
|
||||
|
@ -1402,7 +1442,9 @@ lws_change_pollfd(struct lws *wsi, int _and, int _or);
|
|||
|
||||
#ifndef LWS_NO_SERVER
|
||||
int lws_context_init_server(struct lws_context_creation_info *info,
|
||||
struct lws_context *context);
|
||||
struct lws_vhost *vhost);
|
||||
LWS_EXTERN struct lws_vhost *
|
||||
lws_select_vhost(struct lws_context *context, int port, const char *servername);
|
||||
LWS_EXTERN int
|
||||
handshake_0405(struct lws_context *context, struct lws *wsi);
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
|
@ -1445,7 +1487,9 @@ enum lws_ssl_capable_status {
|
|||
#define lws_server_socket_service_ssl(_b, _c) (0)
|
||||
#define lws_ssl_close(_a) (0)
|
||||
#define lws_ssl_context_destroy(_a)
|
||||
#define lws_ssl_SSL_CTX_destroy(_a)
|
||||
#define lws_ssl_remove_wsi_from_buffered_list(_a)
|
||||
#define lws_context_init_ssl_library(_a)
|
||||
#else
|
||||
#define LWS_SSL_ENABLED(context) (context->use_ssl)
|
||||
LWS_EXTERN int openssl_websocket_private_data_index;
|
||||
|
@ -1455,23 +1499,27 @@ LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
|||
lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len);
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
lws_ssl_pending(struct lws *wsi);
|
||||
LWS_EXTERN int
|
||||
lws_context_init_ssl_library(struct lws_context_creation_info *info);
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
lws_server_socket_service_ssl(struct lws *new_wsi, lws_sockfd_type accept_fd);
|
||||
LWS_EXTERN int
|
||||
lws_ssl_close(struct lws *wsi);
|
||||
LWS_EXTERN void
|
||||
lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost);
|
||||
LWS_EXTERN void
|
||||
lws_ssl_context_destroy(struct lws_context *context);
|
||||
LWS_VISIBLE void
|
||||
lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi);
|
||||
#ifndef LWS_NO_SERVER
|
||||
LWS_EXTERN int
|
||||
lws_context_init_server_ssl(struct lws_context_creation_info *info,
|
||||
struct lws_context *context);
|
||||
struct lws_vhost *vhost);
|
||||
#else
|
||||
#define lws_context_init_server_ssl(_a, _b) (0)
|
||||
#endif
|
||||
LWS_EXTERN void
|
||||
lws_ssl_destroy(struct lws_context *context);
|
||||
lws_ssl_destroy(struct lws_vhost *vhost);
|
||||
|
||||
/* HTTP2-related */
|
||||
|
||||
|
@ -1549,7 +1597,7 @@ lws_http_transaction_completed_client(struct lws *wsi);
|
|||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
LWS_EXTERN int
|
||||
lws_context_init_client_ssl(struct lws_context_creation_info *info,
|
||||
struct lws_context *context);
|
||||
struct lws_vhost *vhost);
|
||||
#else
|
||||
#define lws_context_init_client_ssl(_a, _b) (0)
|
||||
#endif
|
||||
|
|
|
@ -84,7 +84,7 @@ lws_extension_server_handshake(struct lws *wsi, char **p)
|
|||
|
||||
/* check a client's extension against our support */
|
||||
|
||||
ext = lws_get_context(wsi)->extensions;
|
||||
ext = wsi->vhost->extensions;
|
||||
|
||||
while (ext && ext->callback) {
|
||||
|
||||
|
@ -106,7 +106,7 @@ lws_extension_server_handshake(struct lws *wsi, char **p)
|
|||
* ask user code if it's OK to apply it on this
|
||||
* particular connection + protocol
|
||||
*/
|
||||
m = lws_get_context(wsi)->protocols[0].callback(wsi,
|
||||
m = wsi->vhost->protocols[0].callback(wsi,
|
||||
LWS_CALLBACK_CONFIRM_EXTENSION_OKAY,
|
||||
wsi->user_space, ext_name, 0);
|
||||
|
||||
|
|
185
lib/server.c
185
lib/server.c
|
@ -24,12 +24,13 @@
|
|||
|
||||
int
|
||||
lws_context_init_server(struct lws_context_creation_info *info,
|
||||
struct lws_context *context)
|
||||
struct lws_vhost *vhost)
|
||||
{
|
||||
#ifdef LWS_POSIX
|
||||
int n, opt = 1, limit = 1;
|
||||
#endif
|
||||
lws_sockfd_type sockfd;
|
||||
struct lws_vhost *vh;
|
||||
struct lws *wsi;
|
||||
int m = 0;
|
||||
|
||||
|
@ -38,9 +39,25 @@ lws_context_init_server(struct lws_context_creation_info *info,
|
|||
if (info->port == CONTEXT_PORT_NO_LISTEN)
|
||||
return 0;
|
||||
|
||||
vh = vhost->context->vhost_list;
|
||||
while (vh) {
|
||||
if (vh->listen_port == info->port) {
|
||||
if ((!info->iface && !vh->iface) ||
|
||||
(info->iface && vh->iface &&
|
||||
!strcmp(info->iface, vh->iface))) {
|
||||
vhost->listen_port = info->port;
|
||||
vhost->iface = info->iface;
|
||||
lwsl_notice(" using listen skt from vhost %s\n",
|
||||
vh->name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
vh = vh->vhost_next;
|
||||
}
|
||||
|
||||
#if LWS_POSIX
|
||||
#if defined(__linux__)
|
||||
limit = context->count_threads;
|
||||
limit = vhost->context->count_threads;
|
||||
#endif
|
||||
|
||||
for (m = 0; m < limit; m++) {
|
||||
|
@ -70,7 +87,7 @@ lws_context_init_server(struct lws_context_creation_info *info,
|
|||
return 1;
|
||||
}
|
||||
#if defined(__linux__) && defined(SO_REUSEPORT) && LWS_MAX_SMP > 1
|
||||
if (context->count_threads > 1)
|
||||
if (vhost->context->count_threads > 1)
|
||||
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT,
|
||||
(const void *)&opt, sizeof(opt)) < 0) {
|
||||
compatible_close(sockfd);
|
||||
|
@ -78,33 +95,36 @@ lws_context_init_server(struct lws_context_creation_info *info,
|
|||
}
|
||||
#endif
|
||||
#endif
|
||||
lws_plat_set_socket_options(context, sockfd);
|
||||
lws_plat_set_socket_options(vhost, sockfd);
|
||||
|
||||
#if LWS_POSIX
|
||||
n = lws_socket_bind(context, sockfd, info->port, info->iface);
|
||||
n = lws_socket_bind(vhost->context, sockfd, info->port, info->iface);
|
||||
if (n < 0)
|
||||
goto bail;
|
||||
info->port = n;
|
||||
#endif
|
||||
context->listen_port = info->port;
|
||||
vhost->listen_port = info->port;
|
||||
vhost->iface = info->iface;
|
||||
|
||||
wsi = lws_zalloc(sizeof(struct lws));
|
||||
if (wsi == NULL) {
|
||||
lwsl_err("Out of mem\n");
|
||||
goto bail;
|
||||
}
|
||||
wsi->context = context;
|
||||
wsi->context = vhost->context;
|
||||
wsi->sock = sockfd;
|
||||
wsi->mode = LWSCM_SERVER_LISTENER;
|
||||
wsi->protocol = context->protocols;
|
||||
wsi->protocol = vhost->protocols;
|
||||
wsi->tsi = m;
|
||||
wsi->vhost = vhost;
|
||||
wsi->listener = 1;
|
||||
|
||||
context->pt[m].wsi_listening = wsi;
|
||||
if (insert_wsi_socket_into_fds(context, wsi))
|
||||
vhost->context->pt[m].wsi_listening = wsi;
|
||||
if (insert_wsi_socket_into_fds(vhost->context, wsi))
|
||||
goto bail;
|
||||
|
||||
context->count_wsi_allocated++;
|
||||
context->pt[m].lserv_fd = sockfd;
|
||||
vhost->context->count_wsi_allocated++;
|
||||
vhost->lserv_wsi = wsi;
|
||||
|
||||
#if LWS_POSIX
|
||||
listen(wsi->sock, LWS_SOMAXCONN);
|
||||
|
@ -112,7 +132,8 @@ lws_context_init_server(struct lws_context_creation_info *info,
|
|||
#else
|
||||
mbed3_tcp_stream_bind(wsi->sock, info->port, wsi);
|
||||
#endif
|
||||
lwsl_notice(" Listening on port %d\n", info->port);
|
||||
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
|
||||
lwsl_notice(" Listening on port %d\n", info->port);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -143,6 +164,69 @@ _lws_server_listen_accept_flow_control(struct lws *twsi, int on)
|
|||
return n;
|
||||
}
|
||||
|
||||
struct lws_vhost *
|
||||
lws_select_vhost(struct lws_context *context, int port, const char *servername)
|
||||
{
|
||||
struct lws_vhost *vhost = context->vhost_list;
|
||||
|
||||
while (vhost) {
|
||||
if (port == vhost->listen_port &&
|
||||
!strcmp(vhost->name, servername)) {
|
||||
lwsl_info("SNI: Found: %s\n", servername);
|
||||
return vhost;
|
||||
}
|
||||
vhost = vhost->vhost_next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char * get_mimetype(const char *file)
|
||||
{
|
||||
int n = strlen(file);
|
||||
|
||||
if (n < 5)
|
||||
return NULL;
|
||||
|
||||
if (!strcmp(&file[n - 4], ".ico"))
|
||||
return "image/x-icon";
|
||||
|
||||
if (!strcmp(&file[n - 4], ".png"))
|
||||
return "image/png";
|
||||
|
||||
if (!strcmp(&file[n - 5], ".html"))
|
||||
return "text/html";
|
||||
|
||||
if (!strcmp(&file[n - 4], ".css"))
|
||||
return "text/css";
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int lws_http_serve(struct lws *wsi, char *uri, const char *origin)
|
||||
{
|
||||
const char *mimetype;
|
||||
char path[256];
|
||||
int n;
|
||||
|
||||
lwsl_notice("%s: %s %s\n", __func__, uri, origin);
|
||||
snprintf(path, sizeof(path) - 1, "%s/%s", origin, uri);
|
||||
|
||||
mimetype = get_mimetype(path);
|
||||
if (!mimetype) {
|
||||
lwsl_err("unknown mimetype for %s", path);
|
||||
lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
n = lws_serve_http_file(wsi, path, mimetype, NULL, 0);
|
||||
if (n < 0)
|
||||
if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi)))
|
||||
return -1; /* error or can't reuse connection: close the socket */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_http_action(struct lws *wsi)
|
||||
{
|
||||
|
@ -152,6 +236,7 @@ lws_http_action(struct lws *wsi)
|
|||
enum http_connection_type connection_type;
|
||||
enum http_version request_version;
|
||||
char content_length_str[32];
|
||||
struct lws_http_mount *hm;
|
||||
unsigned int n, count = 0;
|
||||
char http_version_str[10];
|
||||
char http_conn_str[20];
|
||||
|
@ -280,7 +365,7 @@ lws_http_action(struct lws *wsi)
|
|||
goto bail_nuke_ah;
|
||||
if (lws_add_http_header_status(wsi, 301, &p, end))
|
||||
goto bail_nuke_ah;
|
||||
n = sprintf((char *)end, "https://%s/",
|
||||
n = sprintf((char *)end, "htt struct lws_http_mount *hm;ps://%s/",
|
||||
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
|
||||
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_LOCATION,
|
||||
end, n, &p, end))
|
||||
|
@ -295,7 +380,28 @@ lws_http_action(struct lws *wsi)
|
|||
}
|
||||
#endif
|
||||
|
||||
n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
|
||||
/* can we serve it from the mount list? */
|
||||
|
||||
hm = wsi->vhost->mount_list;
|
||||
while (hm) {
|
||||
char *s = uri_ptr + hm->mountpoint_len;
|
||||
|
||||
if (s[0] == '\0')
|
||||
s = (char *)hm->def;
|
||||
|
||||
if (!s)
|
||||
s = "index.html";
|
||||
|
||||
if (uri_len >= hm->mountpoint_len &&
|
||||
!strncmp(uri_ptr, hm->mountpoint, hm->mountpoint_len)) {
|
||||
n = lws_http_serve(wsi, s, hm->origin);
|
||||
break;
|
||||
}
|
||||
hm = hm->mount_next;
|
||||
}
|
||||
|
||||
if (!hm)
|
||||
n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
|
||||
wsi->user_space, uri_ptr, uri_len);
|
||||
if (n) {
|
||||
lwsl_info("LWS_CALLBACK_HTTP closing\n");
|
||||
|
@ -387,13 +493,25 @@ lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
|
|||
lwsl_info("No upgrade\n");
|
||||
ah = wsi->u.hdr.ah;
|
||||
|
||||
/* select vhost */
|
||||
|
||||
if (lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
|
||||
struct lws_vhost *vhost = lws_select_vhost(
|
||||
context, wsi->vhost->listen_port,
|
||||
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
|
||||
|
||||
if (vhost)
|
||||
wsi->vhost = vhost;
|
||||
}
|
||||
|
||||
lws_union_transition(wsi, LWSCM_HTTP_SERVING_ACCEPTED);
|
||||
wsi->state = LWSS_HTTP;
|
||||
wsi->u.http.fd = LWS_INVALID_FILE;
|
||||
|
||||
/* expose it at the same offset as u.hdr */
|
||||
wsi->u.http.ah = ah;
|
||||
lwsl_debug("%s: wsi %p: ah %p\n", __func__, (void *)wsi, (void *)wsi->u.hdr.ah);
|
||||
lwsl_debug("%s: wsi %p: ah %p\n", __func__, (void *)wsi,
|
||||
(void *)wsi->u.hdr.ah);
|
||||
|
||||
n = lws_http_action(wsi);
|
||||
|
||||
|
@ -485,12 +603,12 @@ upgrade_ws:
|
|||
lwsl_info("checking %s\n", protocol_name);
|
||||
|
||||
n = 0;
|
||||
while (context->protocols[n].callback) {
|
||||
if (context->protocols[n].name &&
|
||||
!strcmp(context->protocols[n].name,
|
||||
while (wsi->vhost->protocols[n].callback) {
|
||||
if (wsi->vhost->protocols[n].name &&
|
||||
!strcmp(wsi->vhost->protocols[n].name,
|
||||
protocol_name)) {
|
||||
lwsl_info("prot match %d\n", n);
|
||||
wsi->protocol = &context->protocols[n];
|
||||
wsi->protocol = &wsi->vhost->protocols[n];
|
||||
hit = 1;
|
||||
break;
|
||||
}
|
||||
|
@ -513,7 +631,7 @@ upgrade_ws:
|
|||
* allow it and match to protocol 0
|
||||
*/
|
||||
lwsl_info("defaulting to prot 0 handler\n");
|
||||
wsi->protocol = &context->protocols[0];
|
||||
wsi->protocol = &wsi->vhost->protocols[0];
|
||||
}
|
||||
|
||||
/* allocate wsi->user storage */
|
||||
|
@ -643,10 +761,10 @@ lws_get_idlest_tsi(struct lws_context *context)
|
|||
}
|
||||
|
||||
struct lws *
|
||||
lws_create_new_server_wsi(struct lws_context *context)
|
||||
lws_create_new_server_wsi(struct lws_vhost *vhost)
|
||||
{
|
||||
struct lws *new_wsi;
|
||||
int n = lws_get_idlest_tsi(context);
|
||||
int n = lws_get_idlest_tsi(vhost->context);
|
||||
|
||||
if (n < 0) {
|
||||
lwsl_err("no space for new conn\n");
|
||||
|
@ -662,7 +780,8 @@ lws_create_new_server_wsi(struct lws_context *context)
|
|||
new_wsi->tsi = n;
|
||||
lwsl_info("Accepted %p to tsi %d\n", new_wsi, new_wsi->tsi);
|
||||
|
||||
new_wsi->context = context;
|
||||
new_wsi->vhost = vhost;
|
||||
new_wsi->context = vhost->context;
|
||||
new_wsi->pending_timeout = NO_PENDING_TIMEOUT;
|
||||
new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
|
||||
|
||||
|
@ -673,7 +792,7 @@ lws_create_new_server_wsi(struct lws_context *context)
|
|||
new_wsi->hdr_parsing_completed = 0;
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
new_wsi->use_ssl = LWS_SSL_ENABLED(context);
|
||||
new_wsi->use_ssl = LWS_SSL_ENABLED(vhost);
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -682,17 +801,17 @@ lws_create_new_server_wsi(struct lws_context *context)
|
|||
* to the start of the supported list, so it can look
|
||||
* for matching ones during the handshake
|
||||
*/
|
||||
new_wsi->protocol = context->protocols;
|
||||
new_wsi->protocol = vhost->protocols;
|
||||
new_wsi->user_space = NULL;
|
||||
new_wsi->ietf_spec_revision = 0;
|
||||
new_wsi->sock = LWS_SOCK_INVALID;
|
||||
context->count_wsi_allocated++;
|
||||
vhost->context->count_wsi_allocated++;
|
||||
|
||||
/*
|
||||
* outermost create notification for wsi
|
||||
* no user_space because no protocol selection
|
||||
*/
|
||||
context->protocols[0].callback(new_wsi, LWS_CALLBACK_WSI_CREATE,
|
||||
vhost->protocols[0].callback(new_wsi, LWS_CALLBACK_WSI_CREATE,
|
||||
NULL, NULL, 0);
|
||||
|
||||
return new_wsi;
|
||||
|
@ -772,7 +891,7 @@ lws_http_transaction_completed(struct lws *wsi)
|
|||
LWS_VISIBLE struct lws *
|
||||
lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd)
|
||||
{
|
||||
struct lws *new_wsi = lws_create_new_server_wsi(context);
|
||||
struct lws *new_wsi = lws_create_new_server_wsi(context->vhost_list);
|
||||
|
||||
if (!new_wsi) {
|
||||
compatible_close(accept_fd);
|
||||
|
@ -796,7 +915,7 @@ lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd)
|
|||
* set properties of the newly created wsi. There's no protocol
|
||||
* selected yet so we issue this to protocols[0]
|
||||
*/
|
||||
if ((context->protocols[0].callback)(new_wsi,
|
||||
if ((context->vhost_list->protocols[0].callback)(new_wsi,
|
||||
LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED, NULL, NULL, 0)) {
|
||||
compatible_close(new_wsi->sock);
|
||||
lws_free(new_wsi);
|
||||
|
@ -806,7 +925,7 @@ lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd)
|
|||
lws_libev_accept(new_wsi, new_wsi->sock);
|
||||
lws_libuv_accept(new_wsi, new_wsi->sock);
|
||||
|
||||
if (!LWS_SSL_ENABLED(context)) {
|
||||
if (!LWS_SSL_ENABLED(new_wsi->vhost)) {
|
||||
if (insert_wsi_socket_into_fds(context, new_wsi))
|
||||
goto fail;
|
||||
} else {
|
||||
|
@ -1113,7 +1232,7 @@ try_pollout:
|
|||
break;
|
||||
}
|
||||
|
||||
lws_plat_set_socket_options(context, accept_fd);
|
||||
lws_plat_set_socket_options(wsi->vhost, accept_fd);
|
||||
|
||||
lwsl_debug("accepted new conn port %u on fd=%d\n",
|
||||
ntohs(cli_addr.sin_port), accept_fd);
|
||||
|
@ -1127,7 +1246,7 @@ try_pollout:
|
|||
* to reject based on client IP. There's no protocol selected
|
||||
* yet so we issue this to protocols[0]
|
||||
*/
|
||||
if ((context->protocols[0].callback)(wsi,
|
||||
if ((wsi->vhost->protocols[0].callback)(wsi,
|
||||
LWS_CALLBACK_FILTER_NETWORK_CONNECTION,
|
||||
NULL, (void *)(long)accept_fd, 0)) {
|
||||
lwsl_debug("Callback denied network connection\n");
|
||||
|
|
267
lib/ssl.c
267
lib/ssl.c
|
@ -28,7 +28,8 @@
|
|||
#include <openssl/ecdh.h>
|
||||
#endif
|
||||
|
||||
int openssl_websocket_private_data_index;
|
||||
int openssl_websocket_private_data_index,
|
||||
openssl_SSL_CTX_private_data_index;
|
||||
|
||||
static int
|
||||
lws_context_init_ssl_pem_passwd_cb(char * buf, int size, int rwflag, void *userdata)
|
||||
|
@ -55,13 +56,47 @@ static void lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creatio
|
|||
SSL_CTX_set_default_passwd_cb(ssl_ctx, lws_context_init_ssl_pem_passwd_cb);
|
||||
}
|
||||
|
||||
int
|
||||
lws_context_init_ssl_library(struct lws_context_creation_info *info)
|
||||
{
|
||||
#ifdef USE_WOLFSSL
|
||||
#ifdef USE_OLD_CYASSL
|
||||
lwsl_notice(" Compiled with CyaSSL support\n");
|
||||
#else
|
||||
lwsl_notice(" Compiled with wolfSSL support\n");
|
||||
#endif
|
||||
#else
|
||||
lwsl_notice(" Compiled with OpenSSL support\n");
|
||||
#endif
|
||||
|
||||
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) {
|
||||
lwsl_notice(" SSL disabled: no LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* basic openssl init */
|
||||
|
||||
SSL_library_init();
|
||||
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
|
||||
openssl_websocket_private_data_index =
|
||||
SSL_get_ex_new_index(0, "lws", NULL, NULL, NULL);
|
||||
|
||||
openssl_SSL_CTX_private_data_index = SSL_CTX_get_ex_new_index(0,
|
||||
NULL, NULL, NULL, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef LWS_NO_SERVER
|
||||
static int
|
||||
OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
|
||||
{
|
||||
SSL *ssl;
|
||||
int n;
|
||||
struct lws_context *context;
|
||||
struct lws_vhost *vh;
|
||||
struct lws wsi;
|
||||
|
||||
ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
|
||||
|
@ -71,16 +106,17 @@ OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
|
|||
* !!! nasty openssl requires the index to come as a library-scope
|
||||
* static
|
||||
*/
|
||||
context = SSL_get_ex_data(ssl, openssl_websocket_private_data_index);
|
||||
vh = SSL_get_ex_data(ssl, openssl_websocket_private_data_index);
|
||||
|
||||
/*
|
||||
* give him a fake wsi with context set, so he can use lws_get_context()
|
||||
* in the callback
|
||||
*/
|
||||
memset(&wsi, 0, sizeof(wsi));
|
||||
wsi.context = context;
|
||||
wsi.vhost = vh;
|
||||
wsi.context = vh->context;
|
||||
|
||||
n = context->protocols[0].callback(&wsi,
|
||||
n = vh->protocols[0].callback(&wsi,
|
||||
LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION,
|
||||
x509_ctx, ssl, preverify_ok);
|
||||
|
||||
|
@ -89,7 +125,7 @@ OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
|
|||
}
|
||||
|
||||
static int
|
||||
lws_context_ssl_init_ecdh(struct lws_context *context)
|
||||
lws_context_ssl_init_ecdh(struct lws_vhost *vhost)
|
||||
{
|
||||
#ifdef LWS_SSL_SERVER_WITH_ECDH_CERT
|
||||
EC_KEY *EC_key = NULL;
|
||||
|
@ -97,13 +133,13 @@ lws_context_ssl_init_ecdh(struct lws_context *context)
|
|||
int KeyType;
|
||||
X509 *x;
|
||||
|
||||
if (!lws_check_opt(context->options, LWS_SERVER_OPTION_SSL_ECDH))
|
||||
if (!lws_check_opt(vhost->context->options, LWS_SERVER_OPTION_SSL_ECDH))
|
||||
return 0;
|
||||
|
||||
lwsl_notice(" Using ECDH certificate support\n");
|
||||
|
||||
/* Get X509 certificate from ssl context */
|
||||
x = sk_X509_value(context->ssl_ctx->extra_certs, 0);
|
||||
x = sk_X509_value(vhost->ssl_ctx->extra_certs, 0);
|
||||
if (!x) {
|
||||
lwsl_err("%s: x is NULL\n", __func__);
|
||||
return 1;
|
||||
|
@ -129,7 +165,7 @@ lws_context_ssl_init_ecdh(struct lws_context *context)
|
|||
lwsl_err("%s: ECDH key is NULL \n", __func__);
|
||||
return 1;
|
||||
}
|
||||
SSL_CTX_set_tmp_ecdh(context->ssl_ctx, EC_key);
|
||||
SSL_CTX_set_tmp_ecdh(vhost->ssl_ctx, EC_key);
|
||||
EC_KEY_free(EC_key);
|
||||
#endif
|
||||
return 0;
|
||||
|
@ -137,7 +173,7 @@ lws_context_ssl_init_ecdh(struct lws_context *context)
|
|||
|
||||
static int
|
||||
lws_context_ssl_init_ecdh_curve(struct lws_context_creation_info *info,
|
||||
struct lws_context *context)
|
||||
struct lws_vhost *vhost)
|
||||
{
|
||||
#ifdef LWS_HAVE_OPENSSL_ECDH_H
|
||||
EC_KEY *ecdh;
|
||||
|
@ -158,10 +194,10 @@ lws_context_ssl_init_ecdh_curve(struct lws_context_creation_info *info,
|
|||
lwsl_err("SSL: Unable to create curve '%s'", ecdh_curve);
|
||||
return 1;
|
||||
}
|
||||
SSL_CTX_set_tmp_ecdh(context->ssl_ctx, ecdh);
|
||||
SSL_CTX_set_tmp_ecdh(vhost->ssl_ctx, ecdh);
|
||||
EC_KEY_free(ecdh);
|
||||
|
||||
SSL_CTX_set_options(context->ssl_ctx, SSL_OP_SINGLE_ECDH_USE);
|
||||
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_SINGLE_ECDH_USE);
|
||||
|
||||
lwsl_notice(" SSL ECDH curve '%s'\n", ecdh_curve);
|
||||
#else
|
||||
|
@ -175,18 +211,43 @@ static int
|
|||
lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg)
|
||||
{
|
||||
struct lws_context *context;
|
||||
struct lws_vhost *vhost, *vh;
|
||||
const char *servername;
|
||||
int port;
|
||||
|
||||
if (!ssl)
|
||||
return SSL_TLSEXT_ERR_NOACK;
|
||||
|
||||
context = (struct lws_context *)SSL_CTX_get_ex_data(
|
||||
SSL_get_SSL_CTX(ssl), 0);
|
||||
SSL_get_SSL_CTX(ssl),
|
||||
openssl_SSL_CTX_private_data_index);
|
||||
|
||||
/*
|
||||
* We can only get ssl accepted connections by using a vhost's ssl_ctx
|
||||
* find out which listening one took us and only match vhosts on the
|
||||
* same port.
|
||||
*/
|
||||
vh = context->vhost_list;
|
||||
while (vh) {
|
||||
if (vh->ssl_ctx == SSL_get_SSL_CTX(ssl))
|
||||
break;
|
||||
vh = vh->vhost_next;
|
||||
}
|
||||
|
||||
assert(vh); /* we cannot get an ssl without using a vhost ssl_ctx */
|
||||
port = vh->listen_port;
|
||||
|
||||
servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
|
||||
lwsl_err("ServerName: %s, context = %p\n", servername, context);
|
||||
|
||||
//SSL_set_SSL_CTX(ssl, sslctx);
|
||||
if (servername) {
|
||||
vhost = lws_select_vhost(context, port, servername);
|
||||
if (vhost) {
|
||||
lwsl_info("SNI: Found: %s\n", servername);
|
||||
SSL_set_SSL_CTX(ssl, vhost->ssl_ctx);
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
lwsl_err("SNI: Unknown ServerName: %s\n", servername);
|
||||
}
|
||||
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
@ -194,57 +255,39 @@ lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg)
|
|||
|
||||
LWS_VISIBLE int
|
||||
lws_context_init_server_ssl(struct lws_context_creation_info *info,
|
||||
struct lws_context *context)
|
||||
struct lws_vhost *vhost)
|
||||
{
|
||||
SSL_METHOD *method;
|
||||
struct lws_context *context = vhost->context;
|
||||
struct lws wsi;
|
||||
int error;
|
||||
int n;
|
||||
|
||||
#ifdef USE_WOLFSSL
|
||||
#ifdef USE_OLD_CYASSL
|
||||
lwsl_notice(" Compiled with CyaSSL support\n");
|
||||
#else
|
||||
lwsl_notice(" Compiled with wolfSSL support\n");
|
||||
#endif
|
||||
#else
|
||||
lwsl_notice(" Compiled with OpenSSL support\n");
|
||||
#endif
|
||||
|
||||
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) {
|
||||
lwsl_notice(" SSL disabled: no LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT\n");
|
||||
vhost->use_ssl = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (info->port != CONTEXT_PORT_NO_LISTEN) {
|
||||
|
||||
context->use_ssl = info->ssl_cert_filepath != NULL;
|
||||
vhost->use_ssl = info->ssl_cert_filepath != NULL;
|
||||
|
||||
if (info->ssl_cipher_list)
|
||||
if (vhost->use_ssl && info->ssl_cipher_list)
|
||||
lwsl_notice(" SSL ciphers: '%s'\n", info->ssl_cipher_list);
|
||||
|
||||
if (context->use_ssl)
|
||||
if (vhost->use_ssl)
|
||||
lwsl_notice(" Using SSL mode\n");
|
||||
else
|
||||
lwsl_notice(" Using non-SSL mode\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* give him a fake wsi with context set, so he can use
|
||||
* give him a fake wsi with context + vhost set, so he can use
|
||||
* lws_get_context() in the callback
|
||||
*/
|
||||
memset(&wsi, 0, sizeof(wsi));
|
||||
wsi.context = context;
|
||||
|
||||
/* basic openssl init */
|
||||
|
||||
SSL_library_init();
|
||||
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
|
||||
openssl_websocket_private_data_index =
|
||||
SSL_get_ex_new_index(0, "libwebsockets", NULL, NULL, NULL);
|
||||
wsi.vhost = vhost;
|
||||
wsi.context = vhost->context;
|
||||
|
||||
/*
|
||||
* Firefox insists on SSLv23 not SSLv3
|
||||
|
@ -263,8 +306,8 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info,
|
|||
(char *)context->pt[0].serv_buf));
|
||||
return 1;
|
||||
}
|
||||
context->ssl_ctx = SSL_CTX_new(method); /* create context */
|
||||
if (!context->ssl_ctx) {
|
||||
vhost->ssl_ctx = SSL_CTX_new(method); /* create context */
|
||||
if (!vhost->ssl_ctx) {
|
||||
error = ERR_get_error();
|
||||
lwsl_err("problem creating ssl context %lu: %s\n",
|
||||
error, ERR_error_string(error,
|
||||
|
@ -273,22 +316,19 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info,
|
|||
}
|
||||
|
||||
/* associate the lws context with the SSL_CTX */
|
||||
n = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL);
|
||||
if (n) {
|
||||
lwsl_err("cannot register arg0 on SSL_CTX %d\n", n);
|
||||
return 1;
|
||||
}
|
||||
SSL_CTX_set_ex_data(context->ssl_ctx, 0, context);
|
||||
|
||||
SSL_CTX_set_ex_data(vhost->ssl_ctx,
|
||||
openssl_SSL_CTX_private_data_index, vhost->context);
|
||||
|
||||
/* Disable SSLv2 and SSLv3 */
|
||||
SSL_CTX_set_options(context->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
|
||||
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
|
||||
#ifdef SSL_OP_NO_COMPRESSION
|
||||
SSL_CTX_set_options(context->ssl_ctx, SSL_OP_NO_COMPRESSION);
|
||||
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_NO_COMPRESSION);
|
||||
#endif
|
||||
SSL_CTX_set_options(context->ssl_ctx, SSL_OP_SINGLE_DH_USE);
|
||||
SSL_CTX_set_options(context->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
|
||||
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_SINGLE_DH_USE);
|
||||
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
|
||||
if (info->ssl_cipher_list)
|
||||
SSL_CTX_set_cipher_list(context->ssl_ctx,
|
||||
SSL_CTX_set_cipher_list(vhost->ssl_ctx,
|
||||
info->ssl_cipher_list);
|
||||
|
||||
/* as a server, are we requiring clients to identify themselves? */
|
||||
|
@ -299,17 +339,17 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info,
|
|||
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED))
|
||||
verify_options |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
|
||||
|
||||
SSL_CTX_set_session_id_context(context->ssl_ctx,
|
||||
SSL_CTX_set_session_id_context(vhost->ssl_ctx,
|
||||
(unsigned char *)context, sizeof(void *));
|
||||
|
||||
/* absolutely require the client cert */
|
||||
|
||||
SSL_CTX_set_verify(context->ssl_ctx,
|
||||
SSL_CTX_set_verify(vhost->ssl_ctx,
|
||||
verify_options, OpenSSL_verify_callback);
|
||||
}
|
||||
|
||||
#ifndef OPENSSL_NO_TLSEXT
|
||||
SSL_CTX_set_tlsext_servername_callback(context->ssl_ctx,
|
||||
SSL_CTX_set_tlsext_servername_callback(vhost->ssl_ctx,
|
||||
lws_ssl_server_name_cb);
|
||||
#endif
|
||||
|
||||
|
@ -319,27 +359,29 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info,
|
|||
*/
|
||||
|
||||
if (info->ssl_ca_filepath &&
|
||||
!SSL_CTX_load_verify_locations(context->ssl_ctx,
|
||||
!SSL_CTX_load_verify_locations(vhost->ssl_ctx,
|
||||
info->ssl_ca_filepath, NULL)) {
|
||||
lwsl_err("%s: SSL_CTX_load_verify_locations unhappy\n");
|
||||
lwsl_err("%s: SSL_CTX_load_verify_locations unhappy\n", __func__);
|
||||
}
|
||||
|
||||
if (lws_context_ssl_init_ecdh_curve(info, context))
|
||||
return -1;
|
||||
if (vhost->use_ssl) {
|
||||
if (lws_context_ssl_init_ecdh_curve(info, vhost))
|
||||
return -1;
|
||||
|
||||
context->protocols[0].callback(&wsi,
|
||||
vhost->protocols[0].callback(&wsi,
|
||||
LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS,
|
||||
context->ssl_ctx, NULL, 0);
|
||||
vhost->ssl_ctx, NULL, 0);
|
||||
}
|
||||
|
||||
if (lws_check_opt(info->options, LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT))
|
||||
/* Normally SSL listener rejects non-ssl, optionally allow */
|
||||
context->allow_non_ssl_on_ssl_port = 1;
|
||||
vhost->allow_non_ssl_on_ssl_port = 1;
|
||||
|
||||
if (context->use_ssl) {
|
||||
if (vhost->use_ssl) {
|
||||
/* openssl init for server sockets */
|
||||
|
||||
/* set the local certificate from CertFile */
|
||||
n = SSL_CTX_use_certificate_chain_file(context->ssl_ctx,
|
||||
n = SSL_CTX_use_certificate_chain_file(vhost->ssl_ctx,
|
||||
info->ssl_cert_filepath);
|
||||
if (n != 1) {
|
||||
error = ERR_get_error();
|
||||
|
@ -350,11 +392,11 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info,
|
|||
(char *)context->pt[0].serv_buf));
|
||||
return 1;
|
||||
}
|
||||
lws_ssl_bind_passphrase(context->ssl_ctx, info);
|
||||
lws_ssl_bind_passphrase(vhost->ssl_ctx, info);
|
||||
|
||||
if (info->ssl_private_key_filepath != NULL) {
|
||||
/* set the private key from KeyFile */
|
||||
if (SSL_CTX_use_PrivateKey_file(context->ssl_ctx,
|
||||
if (SSL_CTX_use_PrivateKey_file(vhost->ssl_ctx,
|
||||
info->ssl_private_key_filepath,
|
||||
SSL_FILETYPE_PEM) != 1) {
|
||||
error = ERR_get_error();
|
||||
|
@ -365,21 +407,21 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info,
|
|||
return 1;
|
||||
}
|
||||
} else
|
||||
if (context->protocols[0].callback(&wsi,
|
||||
if (vhost->protocols[0].callback(&wsi,
|
||||
LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY,
|
||||
context->ssl_ctx, NULL, 0)) {
|
||||
vhost->ssl_ctx, NULL, 0)) {
|
||||
lwsl_err("ssl private key not set\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* verify private key */
|
||||
if (!SSL_CTX_check_private_key(context->ssl_ctx)) {
|
||||
if (!SSL_CTX_check_private_key(vhost->ssl_ctx)) {
|
||||
lwsl_err("Private SSL key doesn't match cert\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (lws_context_ssl_init_ecdh(context))
|
||||
if (lws_context_ssl_init_ecdh(vhost))
|
||||
return 1;
|
||||
|
||||
/*
|
||||
|
@ -395,15 +437,15 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info,
|
|||
#endif
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_ssl_destroy(struct lws_context *context)
|
||||
lws_ssl_destroy(struct lws_vhost *vhost)
|
||||
{
|
||||
if (!lws_check_opt(context->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
|
||||
if (!lws_check_opt(vhost->context->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
|
||||
return;
|
||||
|
||||
if (context->ssl_ctx)
|
||||
SSL_CTX_free(context->ssl_ctx);
|
||||
if (!context->user_supplied_ssl_ctx && context->ssl_client_ctx)
|
||||
SSL_CTX_free(context->ssl_client_ctx);
|
||||
if (vhost->ssl_ctx)
|
||||
SSL_CTX_free(vhost->ssl_ctx);
|
||||
if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx)
|
||||
SSL_CTX_free(vhost->ssl_client_ctx);
|
||||
|
||||
#if (OPENSSL_VERSION_NUMBER < 0x01000000) || defined(USE_WOLFSSL)
|
||||
ERR_remove_state(0);
|
||||
|
@ -430,7 +472,7 @@ lws_decode_ssl_error(void)
|
|||
#ifndef LWS_NO_CLIENT
|
||||
|
||||
int lws_context_init_client_ssl(struct lws_context_creation_info *info,
|
||||
struct lws_context *context)
|
||||
struct lws_vhost *vhost)
|
||||
{
|
||||
int error;
|
||||
int n;
|
||||
|
@ -442,9 +484,9 @@ int lws_context_init_client_ssl(struct lws_context_creation_info *info,
|
|||
|
||||
if (info->provided_client_ssl_ctx) {
|
||||
/* use the provided OpenSSL context if given one */
|
||||
context->ssl_client_ctx = info->provided_client_ssl_ctx;
|
||||
vhost->ssl_client_ctx = info->provided_client_ssl_ctx;
|
||||
/* nothing for lib to delete */
|
||||
context->user_supplied_ssl_ctx = 1;
|
||||
vhost->user_supplied_ssl_ctx = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -463,39 +505,39 @@ int lws_context_init_client_ssl(struct lws_context_creation_info *info,
|
|||
error = ERR_get_error();
|
||||
lwsl_err("problem creating ssl method %lu: %s\n",
|
||||
error, ERR_error_string(error,
|
||||
(char *)context->pt[0].serv_buf));
|
||||
(char *)vhost->context->pt[0].serv_buf));
|
||||
return 1;
|
||||
}
|
||||
/* create context */
|
||||
context->ssl_client_ctx = SSL_CTX_new(method);
|
||||
if (!context->ssl_client_ctx) {
|
||||
vhost->ssl_client_ctx = SSL_CTX_new(method);
|
||||
if (!vhost->ssl_client_ctx) {
|
||||
error = ERR_get_error();
|
||||
lwsl_err("problem creating ssl context %lu: %s\n",
|
||||
error, ERR_error_string(error,
|
||||
(char *)context->pt[0].serv_buf));
|
||||
(char *)vhost->context->pt[0].serv_buf));
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef SSL_OP_NO_COMPRESSION
|
||||
SSL_CTX_set_options(context->ssl_client_ctx,
|
||||
SSL_CTX_set_options(vhost->ssl_client_ctx,
|
||||
SSL_OP_NO_COMPRESSION);
|
||||
#endif
|
||||
SSL_CTX_set_options(context->ssl_client_ctx,
|
||||
SSL_CTX_set_options(vhost->ssl_client_ctx,
|
||||
SSL_OP_CIPHER_SERVER_PREFERENCE);
|
||||
if (info->ssl_cipher_list)
|
||||
SSL_CTX_set_cipher_list(context->ssl_client_ctx,
|
||||
SSL_CTX_set_cipher_list(vhost->ssl_client_ctx,
|
||||
info->ssl_cipher_list);
|
||||
|
||||
#ifdef LWS_SSL_CLIENT_USE_OS_CA_CERTS
|
||||
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS))
|
||||
/* loads OS default CA certs */
|
||||
SSL_CTX_set_default_verify_paths(context->ssl_client_ctx);
|
||||
SSL_CTX_set_default_verify_paths(vhost->ssl_client_ctx);
|
||||
#endif
|
||||
|
||||
/* openssl init for cert verification (for client sockets) */
|
||||
if (!info->ssl_ca_filepath) {
|
||||
if (!SSL_CTX_load_verify_locations(
|
||||
context->ssl_client_ctx, NULL,
|
||||
vhost->ssl_client_ctx, NULL,
|
||||
LWS_OPENSSL_CLIENT_CERTS))
|
||||
lwsl_err(
|
||||
"Unable to load SSL Client certs from %s "
|
||||
|
@ -504,7 +546,7 @@ int lws_context_init_client_ssl(struct lws_context_creation_info *info,
|
|||
"going to work", LWS_OPENSSL_CLIENT_CERTS);
|
||||
} else
|
||||
if (!SSL_CTX_load_verify_locations(
|
||||
context->ssl_client_ctx, info->ssl_ca_filepath,
|
||||
vhost->ssl_client_ctx, info->ssl_ca_filepath,
|
||||
NULL))
|
||||
lwsl_err(
|
||||
"Unable to load SSL Client certs "
|
||||
|
@ -520,32 +562,32 @@ int lws_context_init_client_ssl(struct lws_context_creation_info *info,
|
|||
|
||||
/* support for client-side certificate authentication */
|
||||
if (info->ssl_cert_filepath) {
|
||||
n = SSL_CTX_use_certificate_chain_file(context->ssl_client_ctx,
|
||||
n = SSL_CTX_use_certificate_chain_file(vhost->ssl_client_ctx,
|
||||
info->ssl_cert_filepath);
|
||||
if (n != 1) {
|
||||
lwsl_err("problem getting cert '%s' %lu: %s\n",
|
||||
info->ssl_cert_filepath,
|
||||
ERR_get_error(),
|
||||
ERR_error_string(ERR_get_error(),
|
||||
(char *)context->pt[0].serv_buf));
|
||||
(char *)vhost->context->pt[0].serv_buf));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (info->ssl_private_key_filepath) {
|
||||
lws_ssl_bind_passphrase(context->ssl_client_ctx, info);
|
||||
lws_ssl_bind_passphrase(vhost->ssl_client_ctx, info);
|
||||
/* set the private key from KeyFile */
|
||||
if (SSL_CTX_use_PrivateKey_file(context->ssl_client_ctx,
|
||||
if (SSL_CTX_use_PrivateKey_file(vhost->ssl_client_ctx,
|
||||
info->ssl_private_key_filepath, SSL_FILETYPE_PEM) != 1) {
|
||||
lwsl_err("use_PrivateKey_file '%s' %lu: %s\n",
|
||||
info->ssl_private_key_filepath,
|
||||
ERR_get_error(),
|
||||
ERR_error_string(ERR_get_error(),
|
||||
(char *)context->pt[0].serv_buf));
|
||||
(char *)vhost->context->pt[0].serv_buf));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* verify private key */
|
||||
if (!SSL_CTX_check_private_key(context->ssl_client_ctx)) {
|
||||
if (!SSL_CTX_check_private_key(vhost->ssl_client_ctx)) {
|
||||
lwsl_err("Private SSL key doesn't match cert\n");
|
||||
return 1;
|
||||
}
|
||||
|
@ -556,11 +598,12 @@ int lws_context_init_client_ssl(struct lws_context_creation_info *info,
|
|||
* lws_get_context() in the callback
|
||||
*/
|
||||
memset(&wsi, 0, sizeof(wsi));
|
||||
wsi.context = context;
|
||||
wsi.vhost = vhost;
|
||||
wsi.context = vhost->context;
|
||||
|
||||
context->protocols[0].callback(&wsi,
|
||||
vhost->protocols[0].callback(&wsi,
|
||||
LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS,
|
||||
context->ssl_client_ctx, NULL, 0);
|
||||
vhost->ssl_client_ctx, NULL, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -712,13 +755,13 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
|
|||
BIO *bio;
|
||||
#endif
|
||||
|
||||
if (!LWS_SSL_ENABLED(context))
|
||||
if (!LWS_SSL_ENABLED(wsi->vhost))
|
||||
return 0;
|
||||
|
||||
switch (wsi->mode) {
|
||||
case LWSCM_SSL_INIT:
|
||||
|
||||
wsi->ssl = SSL_new(context->ssl_ctx);
|
||||
wsi->ssl = SSL_new(wsi->vhost->ssl_ctx);
|
||||
if (wsi->ssl == NULL) {
|
||||
lwsl_err("SSL_new failed: %s\n",
|
||||
ERR_error_string(SSL_get_error(wsi->ssl, 0), NULL));
|
||||
|
@ -787,7 +830,7 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
|
|||
* it disabled unless you know it's not a problem for you
|
||||
*/
|
||||
|
||||
if (context->allow_non_ssl_on_ssl_port) {
|
||||
if (wsi->vhost->allow_non_ssl_on_ssl_port) {
|
||||
if (n >= 1 && pt->serv_buf[0] >= ' ') {
|
||||
/*
|
||||
* TLS content-type for Handshake is 0x16, and
|
||||
|
@ -876,14 +919,18 @@ fail:
|
|||
return 1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
void
|
||||
lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost)
|
||||
{
|
||||
if (vhost->ssl_ctx)
|
||||
SSL_CTX_free(vhost->ssl_ctx);
|
||||
if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx)
|
||||
SSL_CTX_free(vhost->ssl_client_ctx);
|
||||
}
|
||||
|
||||
void
|
||||
lws_ssl_context_destroy(struct lws_context *context)
|
||||
{
|
||||
if (context->ssl_ctx)
|
||||
SSL_CTX_free(context->ssl_ctx);
|
||||
if (!context->user_supplied_ssl_ctx && context->ssl_client_ctx)
|
||||
SSL_CTX_free(context->ssl_client_ctx);
|
||||
|
||||
#if (OPENSSL_VERSION_NUMBER < 0x01000000) || defined(USE_WOLFSSL)
|
||||
ERR_remove_state(0);
|
||||
#else
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* libwebsockets-test-servet - libwebsockets test implementation
|
||||
* libwebsockets-test-server - libwebsockets test implementation
|
||||
*
|
||||
* Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
|
|
Loading…
Add table
Reference in a new issue