mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
lwsws protocol filter and options
Signed-off-by: Andy Green <andy@warmcat.com>
This commit is contained in:
parent
efcf496b45
commit
37098ae2a2
9 changed files with 252 additions and 49 deletions
|
@ -68,43 +68,67 @@ Listing multiple vhosts looks something like this
|
|||
|
||||
```
|
||||
{
|
||||
"vhosts": [{
|
||||
"name": "warmcat.com",
|
||||
"port": "443",
|
||||
"host-ssl-key": "/etc/pki/tls/private/warmcat.com.key",
|
||||
"host-ssl-cert": "/etc/pki/tls/certs/warmcat.com.crt",
|
||||
"host-ssl-ca": "/etc/pki/tls/certs/warmcat.com.cer",
|
||||
"mounts": [{
|
||||
"mountpoint": "/",
|
||||
"origin": "file:///var/www/warmcat.com",
|
||||
"default": "index.html"
|
||||
}]
|
||||
}, {
|
||||
"name": "warmcat2.com",
|
||||
"port": "443",
|
||||
"host-ssl-key": "/etc/pki/tls/private/warmcat.com.key",
|
||||
"host-ssl-cert": "/etc/pki/tls/certs/warmcat.com.crt",
|
||||
"host-ssl-ca": "/etc/pki/tls/certs/warmcat.com.cer",
|
||||
"mounts": [{
|
||||
"mountpoint": "/",
|
||||
"origin": "file:///var/www/warmcat2.com",
|
||||
"default": "index.html"
|
||||
}]
|
||||
}
|
||||
]
|
||||
"vhosts": [ {
|
||||
"name": "localhost",
|
||||
"port": "443",
|
||||
"host-ssl-key": "/etc/pki/tls/private/libwebsockets.org.key",
|
||||
"host-ssl-cert": "/etc/pki/tls/certs/libwebsockets.org.crt",
|
||||
"host-ssl-ca": "/etc/pki/tls/certs/libwebsockets.org.cer",
|
||||
"mounts": [{
|
||||
"mountpoint": "/",
|
||||
"origin": "file:///var/www/libwebsockets.org",
|
||||
"default": "index.html"
|
||||
}, {
|
||||
"mountpoint": "/testserver",
|
||||
"origin": "file:///usr/local/share/libwebsockets-test-server",
|
||||
"default": "test.html"
|
||||
}],
|
||||
# which protocols are enabled for this vhost, and optional
|
||||
# vhost-specific config options for the protocol
|
||||
#
|
||||
"ws-protocols": [{
|
||||
"warmcat,timezoom": {
|
||||
"status": "ok"
|
||||
}
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name": "localhost",
|
||||
"port": "7681",
|
||||
"host-ssl-key": "/etc/pki/tls/private/libwebsockets.org.key",
|
||||
"host-ssl-cert": "/etc/pki/tls/certs/libwebsockets.org.crt",
|
||||
"host-ssl-ca": "/etc/pki/tls/certs/libwebsockets.org.cer",
|
||||
"mounts": [{
|
||||
"mountpoint": "/",
|
||||
"origin": ">https://localhost"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name": "localhost",
|
||||
"port": "80",
|
||||
"mounts": [{
|
||||
"mountpoint": "/",
|
||||
"origin": ">https://localhost"
|
||||
}]
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
That sets up three vhosts all called "localhost" on ports 443 and 7681 with SSL, and port 80 without SSL but with a forced redirect to https://localhost
|
||||
|
||||
|
||||
Vhost name and port
|
||||
-------------------
|
||||
|
||||
The vhost name field is used to match on incoming SNI or Host: header, so it
|
||||
must always be the host name used to reach the vhost externally.
|
||||
|
||||
Vhosts may have the same name and different ports, these will each create a
|
||||
- Vhosts may have the same name and different ports, these will each create a
|
||||
listening socket on the appropriate port.
|
||||
|
||||
They may also have the same port and different name: these will be treated as
|
||||
- Vhosts may also have the same port and different name: these will be treated as
|
||||
true vhosts on one listening socket and the active vhost decided at SSL
|
||||
negotiation time (via SNI) or if no SSL, then after the Host: header from
|
||||
the client has been parsed.
|
||||
|
@ -148,3 +172,20 @@ To help that happen conveniently, there are some new apis
|
|||
dumb increment, mirror and status protocol plugins are provided as examples.
|
||||
|
||||
|
||||
Protocols
|
||||
---------
|
||||
|
||||
Vhosts by default have available the union of any initial protocols from context creation time, and
|
||||
any protocols exposed by plugins.
|
||||
|
||||
Vhosts can select which plugins they want to offer and give them per-vhost settings using this syntax
|
||||
|
||||
```
|
||||
"ws-protocols": [{
|
||||
"warmcat,timezoom": {
|
||||
"status": "ok"
|
||||
}
|
||||
}]
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -129,10 +129,26 @@ lws_protocol_vh_priv_get(struct lws_vhost *vhost, const struct lws_protocols *pr
|
|||
return vhost->protocol_vh_privs[n];
|
||||
}
|
||||
|
||||
static struct lws_protocol_vhost_options *
|
||||
lws_vhost_protocol_options(struct lws_vhost *vh, const char *name)
|
||||
{
|
||||
struct lws_protocol_vhost_options *pvo = vh->pvo;
|
||||
|
||||
while (pvo) {
|
||||
// lwsl_notice("%s: '%s' '%s'\n", __func__, pvo->name, name);
|
||||
if (!strcmp(pvo->name, name))
|
||||
return pvo;
|
||||
pvo = pvo->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
lws_protocol_init(struct lws_context *context)
|
||||
{
|
||||
struct lws_vhost *vh = context->vhost_list;
|
||||
struct lws_protocol_vhost_options *pvo;
|
||||
struct lws wsi;
|
||||
int n;
|
||||
|
||||
|
@ -147,6 +163,15 @@ lws_protocol_init(struct lws_context *context)
|
|||
for (n = 0; n < vh->count_protocols; n++) {
|
||||
wsi.protocol = &vh->protocols[n];
|
||||
|
||||
pvo = lws_vhost_protocol_options(vh,
|
||||
vh->protocols[n].name);
|
||||
if (pvo)
|
||||
/*
|
||||
* linked list of options specific to
|
||||
* vh + protocol
|
||||
*/
|
||||
pvo = pvo->options;
|
||||
|
||||
/*
|
||||
* inform all the protocols that they are doing their one-time
|
||||
* initialization if they want to.
|
||||
|
@ -155,7 +180,7 @@ lws_protocol_init(struct lws_context *context)
|
|||
* protocol ptrs so lws_get_context(wsi) etc can work
|
||||
*/
|
||||
vh->protocols[n].callback(&wsi,
|
||||
LWS_CALLBACK_PROTOCOL_INIT, NULL, NULL, 0);
|
||||
LWS_CALLBACK_PROTOCOL_INIT, NULL, pvo, 0);
|
||||
}
|
||||
|
||||
vh = vh->vhost_next;
|
||||
|
@ -176,7 +201,7 @@ lws_create_vhost(struct lws_context *context,
|
|||
#ifdef LWS_WITH_PLUGINS
|
||||
struct lws_plugin *plugin = context->plugin_list;
|
||||
struct lws_protocols *lwsp;
|
||||
int m;
|
||||
int m, n;
|
||||
#endif
|
||||
char *p;
|
||||
|
||||
|
@ -194,6 +219,9 @@ lws_create_vhost(struct lws_context *context,
|
|||
info->protocols[vh->count_protocols].callback;
|
||||
vh->count_protocols++)
|
||||
;
|
||||
|
||||
vh->pvo = info->pvo;
|
||||
|
||||
#ifdef LWS_WITH_PLUGINS
|
||||
if (plugin) {
|
||||
/*
|
||||
|
@ -209,12 +237,22 @@ lws_create_vhost(struct lws_context *context,
|
|||
m = vh->count_protocols;
|
||||
memcpy(lwsp, info->protocols,
|
||||
sizeof(struct lws_protocols) * m);
|
||||
|
||||
while (plugin) {
|
||||
memcpy(&lwsp[m], plugin->caps.protocols,
|
||||
sizeof(struct lws_protocols) *
|
||||
plugin->caps.count_protocols);
|
||||
m += plugin->caps.count_protocols;
|
||||
vh->count_protocols += plugin->caps.count_protocols;
|
||||
for (n = 0; n < plugin->caps.count_protocols; n++) {
|
||||
/*
|
||||
* for compatibility's sake, no pvo implies
|
||||
* allow all protocols
|
||||
*/
|
||||
if (!info->pvo || lws_vhost_protocol_options(vh,
|
||||
plugin->caps.protocols[n].name)) {
|
||||
memcpy(&lwsp[m],
|
||||
&plugin->caps.protocols[n],
|
||||
sizeof(struct lws_protocols));
|
||||
m++;
|
||||
vh->count_protocols++;
|
||||
}
|
||||
}
|
||||
plugin = plugin->list;
|
||||
}
|
||||
vh->protocols = lwsp;
|
||||
|
@ -222,7 +260,6 @@ lws_create_vhost(struct lws_context *context,
|
|||
#endif
|
||||
vh->protocols = info->protocols;
|
||||
|
||||
|
||||
vh->mount_list = mounts;
|
||||
|
||||
lwsl_notice("Creating Vhost '%s' port %d, %d protocols\n",
|
||||
|
|
|
@ -1339,6 +1339,12 @@ LWS_EXTERN int
|
|||
lws_set_extension_option(struct lws *wsi, const char *ext_name,
|
||||
const char *opt_name, const char *opt_val);
|
||||
|
||||
struct lws_protocol_vhost_options {
|
||||
struct lws_protocol_vhost_options *next;
|
||||
struct lws_protocol_vhost_options *options;
|
||||
const char *name;
|
||||
const char *value;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct lws_context_creation_info - parameters to create context with
|
||||
|
@ -1451,6 +1457,7 @@ struct lws_context_creation_info {
|
|||
const char *ecdh_curve; /* VH */
|
||||
const char *vhost_name; /* VH */
|
||||
const char *plugins_dir; /* context */
|
||||
struct lws_protocol_vhost_options *pvo; /* VH */
|
||||
|
||||
/* Add new things just above here ---^
|
||||
* This is part of the ABI, don't needlessly break compatibility
|
||||
|
|
|
@ -654,6 +654,7 @@ struct lws_vhost {
|
|||
const char *iface;
|
||||
const struct lws_protocols *protocols;
|
||||
void **protocol_vh_privs;
|
||||
struct lws_protocol_vhost_options *pvo;
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
SSL_CTX *ssl_ctx;
|
||||
SSL_CTX *ssl_client_ctx;
|
||||
|
|
34
lib/server.c
34
lib/server.c
|
@ -239,13 +239,13 @@ 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;
|
||||
struct lws_http_mount *hm, *hit = NULL;
|
||||
unsigned int n, count = 0;
|
||||
char http_version_str[10];
|
||||
char http_conn_str[20];
|
||||
int http_version_len;
|
||||
char *uri_ptr = NULL;
|
||||
int uri_len = 0;
|
||||
int uri_len = 0, best = 0;
|
||||
|
||||
static const unsigned char methods[] = {
|
||||
WSI_TOKEN_GET_URI,
|
||||
|
@ -395,25 +395,33 @@ lws_http_action(struct lws *wsi)
|
|||
|
||||
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";
|
||||
|
||||
lwsl_err("a %p\n", hm);
|
||||
if (uri_len >= hm->mountpoint_len &&
|
||||
!strncmp(uri_ptr, hm->mountpoint, hm->mountpoint_len)) {
|
||||
n = lws_http_serve(wsi, s, hm->origin);
|
||||
break;
|
||||
if (hm->mountpoint_len > best) {
|
||||
best = hm->mountpoint_len;
|
||||
hit = hm;
|
||||
}
|
||||
}
|
||||
hm = hm->mount_next;
|
||||
}
|
||||
lwsl_err("x\n");
|
||||
if (hit) {
|
||||
char *s = uri_ptr + hit->mountpoint_len;
|
||||
|
||||
if (s[0] == '\0')
|
||||
s = (char *)hit->def;
|
||||
|
||||
if (!s)
|
||||
s = "index.html";
|
||||
lwsl_err("b\n");
|
||||
n = lws_http_serve(wsi, s, hit->origin);
|
||||
} else {
|
||||
lwsl_err("c\n");
|
||||
|
||||
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");
|
||||
|
||||
|
|
65
lwsws/conf.c
65
lwsws/conf.c
|
@ -47,7 +47,10 @@ static const char * const paths_vhosts[] = {
|
|||
"vhosts[].host-ssl-ca",
|
||||
"vhosts[].mounts[].mountpoint",
|
||||
"vhosts[].mounts[].origin",
|
||||
"vhosts[].mounts[].default"
|
||||
"vhosts[].mounts[].default",
|
||||
"vhosts[].ws-protocols[].*.*",
|
||||
"vhosts[].ws-protocols[].*",
|
||||
"vhosts[].ws-protocols[]"
|
||||
};
|
||||
|
||||
enum lejp_vhost_paths {
|
||||
|
@ -61,6 +64,9 @@ enum lejp_vhost_paths {
|
|||
LEJPVP_MOUNTPOINT,
|
||||
LEJPVP_ORIGIN,
|
||||
LEJPVP_DEFAULT,
|
||||
LEJPVP_PROTOCOL_NAME_OPT,
|
||||
LEJPVP_PROTOCOL_NAME,
|
||||
LEJPVP_PROTOCOL,
|
||||
};
|
||||
|
||||
struct jpargs {
|
||||
|
@ -71,8 +77,18 @@ struct jpargs {
|
|||
char *p, *end, valid;
|
||||
struct lws_http_mount *head, *last;
|
||||
char *mountpoint, *origin, *def;
|
||||
struct lws_protocol_vhost_options *pvo;
|
||||
};
|
||||
|
||||
static void *
|
||||
lwsws_align(struct jpargs *a)
|
||||
{
|
||||
if ((unsigned long)(a->p) & 15)
|
||||
a->p += 16 - ((unsigned long)(a->p) & 15);
|
||||
|
||||
return a->p;
|
||||
}
|
||||
|
||||
static int arg_to_bool(const char *s)
|
||||
{
|
||||
static const char * const on[] = { "on", "yes", "true" };
|
||||
|
@ -128,10 +144,16 @@ static char
|
|||
lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
|
||||
{
|
||||
struct jpargs *a = (struct jpargs *)ctx->user;
|
||||
struct lws_protocol_vhost_options *pvo;
|
||||
struct lws_http_mount *m;
|
||||
int n;
|
||||
|
||||
// lwsl_notice(" %d: %s (%d)\n", reason, ctx->path, ctx->path_match);
|
||||
// for (n = 0; n < ctx->wildcount; n++)
|
||||
// lwsl_notice(" %d\n", ctx->wild[n]);
|
||||
|
||||
if (reason == LEJPCB_OBJECT_START && ctx->path_match == LEJPVP + 1) {
|
||||
/* set the defaults for this vhost */
|
||||
a->valid = 1;
|
||||
a->head = NULL;
|
||||
a->last = NULL;
|
||||
|
@ -156,6 +178,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
|
|||
"!DHE-RSA-AES256-SHA256:"
|
||||
"!AES256-GCM-SHA384:"
|
||||
"!AES256-SHA256";
|
||||
a->info->pvo = NULL;
|
||||
}
|
||||
|
||||
if (reason == LEJPCB_OBJECT_START &&
|
||||
|
@ -165,6 +188,25 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
|
|||
a->def = NULL;
|
||||
}
|
||||
|
||||
/* this catches, eg, vhosts[].ws-protocols[].xxx-protocol */
|
||||
if (reason == LEJPCB_OBJECT_START &&
|
||||
ctx->path_match == LEJPVP_PROTOCOL_NAME + 1) {
|
||||
a->pvo = lwsws_align(a);
|
||||
a->p += sizeof(*a->pvo);
|
||||
|
||||
n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
|
||||
/* ie, enable this protocol, no options yet */
|
||||
a->pvo->next = a->info->pvo;
|
||||
a->info->pvo = a->pvo;
|
||||
a->pvo->name = a->p;
|
||||
lwsl_err("adding %s\n", a->p);
|
||||
a->p += n;
|
||||
a->pvo->value = a->p;
|
||||
a->pvo->options = NULL;
|
||||
a->p += snprintf(a->p, a->end - a->p, "%s", ctx->buf);
|
||||
*(a->p)++ = '\0';
|
||||
}
|
||||
|
||||
if (reason == LEJPCB_OBJECT_END &&
|
||||
(ctx->path_match == LEJPVP + 1 || !ctx->path[0]) &&
|
||||
a->valid) {
|
||||
|
@ -232,6 +274,27 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
|
|||
case LEJPVP_DEFAULT:
|
||||
a->def = a->p;
|
||||
break;
|
||||
|
||||
case LEJPVP_PROTOCOL_NAME_OPT:
|
||||
/* this catches, eg,
|
||||
* vhosts[].ws-protocols[].xxx-protocol.yyy-option
|
||||
* ie, these are options attached to a protocol with { }
|
||||
*/
|
||||
pvo = lwsws_align(a);
|
||||
a->p += sizeof(*a->pvo);
|
||||
|
||||
n = lejp_get_wildcard(ctx, 1, a->p, a->end - a->p);
|
||||
/* ie, enable this protocol, no options yet */
|
||||
pvo->next = a->pvo->options;
|
||||
a->pvo->options = pvo;
|
||||
pvo->name = a->p;
|
||||
a->p += n;
|
||||
pvo->value = a->p;
|
||||
pvo->options = NULL;
|
||||
a->p += snprintf(a->p, a->end - a->p, "%s", ctx->buf);
|
||||
*(a->p)++ = '\0';
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
|
42
lwsws/lejp.c
42
lwsws/lejp.c
|
@ -97,16 +97,56 @@ lejp_change_callback(struct lejp_ctx *ctx,
|
|||
static void
|
||||
lejp_check_path_match(struct lejp_ctx *ctx)
|
||||
{
|
||||
const char *p, *q;
|
||||
int n;
|
||||
|
||||
/* we only need to check if a match is not active */
|
||||
for (n = 0; !ctx->path_match && n < ctx->count_paths; n++) {
|
||||
if (strcmp(ctx->path, ctx->paths[n]))
|
||||
ctx->wildcount = 0;
|
||||
p = ctx->path;
|
||||
q = ctx->paths[n];
|
||||
while (*p && *q) {
|
||||
if (*q != '*') {
|
||||
if (*p != *q)
|
||||
break;
|
||||
p++;
|
||||
q++;
|
||||
continue;
|
||||
}
|
||||
ctx->wild[ctx->wildcount++] = p - ctx->path;
|
||||
q++;
|
||||
while (*p && *p != '.')
|
||||
p++;
|
||||
}
|
||||
if (*p || *q)
|
||||
continue;
|
||||
|
||||
ctx->path_match = n + 1;
|
||||
ctx->path_match_len = ctx->ppos;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ctx->path_match)
|
||||
ctx->wildcount = 0;
|
||||
}
|
||||
|
||||
int
|
||||
lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (wildcard >= ctx->wildcount || !len)
|
||||
return 0;
|
||||
|
||||
n = ctx->wild[wildcard];
|
||||
|
||||
while (--len && n < ctx->ppos && ctx->path[n] != '.')
|
||||
*dest++ = ctx->path[n++];
|
||||
|
||||
*dest = '\0';
|
||||
n++;
|
||||
|
||||
return n - ctx->wild[wildcard];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -187,6 +187,7 @@ struct lejp_ctx {
|
|||
|
||||
struct _lejp_stack st[LEJP_MAX_DEPTH];
|
||||
unsigned short i[LEJP_MAX_INDEX_DEPTH]; /* index array */
|
||||
unsigned short wild[LEJP_MAX_INDEX_DEPTH]; /* index array */
|
||||
char path[LEJP_MAX_PATH];
|
||||
char buf[LEJP_STRING_CHUNK];
|
||||
|
||||
|
@ -209,6 +210,7 @@ struct lejp_ctx {
|
|||
unsigned char count_paths;
|
||||
unsigned char path_match;
|
||||
unsigned char path_match_len;
|
||||
unsigned char wildcount;
|
||||
};
|
||||
|
||||
extern void
|
||||
|
@ -225,3 +227,6 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len);
|
|||
extern void
|
||||
lejp_change_callback(struct lejp_ctx *ctx,
|
||||
char (*callback)(struct lejp_ctx *ctx, char reason));
|
||||
|
||||
extern int
|
||||
lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len);
|
||||
|
|
|
@ -58,6 +58,7 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
|
||||
switch (reason) {
|
||||
case LWS_CALLBACK_PROTOCOL_INIT:
|
||||
lwsl_notice("%s: pvo %p\n", __func__, in);
|
||||
vhd = lws_protocol_vh_priv_zalloc(lws_vhost_get(wsi),
|
||||
lws_protocol_get(wsi),
|
||||
sizeof(struct per_vhost_data__dumb_increment));
|
||||
|
|
Loading…
Add table
Reference in a new issue