1
0
Fork 0
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:
Andy Green 2016-04-08 13:25:34 +08:00
parent efcf496b45
commit 37098ae2a2
9 changed files with 252 additions and 49 deletions

View file

@ -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"
}
}]
```

View file

@ -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",

View file

@ -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

View file

@ -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;

View file

@ -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");

View file

@ -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;
}

View file

@ -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];
}
/**

View file

@ -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);

View file

@ -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));