1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

add protocol plugin for post demo

Signed-off-by: Andy Green <andy@warmcat.com>
This commit is contained in:
Andy Green 2016-05-09 09:37:01 +08:00
parent 4010694d04
commit b24aaeb822
11 changed files with 323 additions and 22 deletions

View file

@ -1216,6 +1216,8 @@ if (NOT LWS_WITHOUT_TESTAPPS)
"plugins/protocol_lws_mirror.c")
create_plugin(protocol_lws_status
"plugins/protocol_lws_status.c")
create_plugin(protocol_post_demo
"plugins/protocol_post_demo.c")
if (LWS_WITH_SERVER_STATUS)
create_plugin(protocol_lws_server_status
"plugins/protocol_lws_server_status.c")

View file

@ -542,11 +542,64 @@ enum {
LWSMPRO_CGI,
LWSMPRO_REDIR_HTTP,
LWSMPRO_REDIR_HTTPS,
LWSMPRO_CALLBACK,
};
```
LWSMPRO_FILE is used for mapping url namespace to a filesystem directory and
serve it automatically.
LWSMPRO_CGI associates the url namespace with the given CGI executable, which
runs when the URL is accessed and the output provided to the client.
LWSMPRO_REDIR_HTTP and LWSMPRO_REDIR_HTTPS auto-redirect clients to the given
origin URL.
LWSMPRO_CALLBACK causes the http connection to attach to the callback
associated with the named protocol (which may be a plugin).
Operation of LWSMPRO_CALLBACK mounts
------------------------------------
The feature provided by CALLBACK type mounts is binding a part of the URL
namespace to a named protocol callback handler.
This allows protocol plugins to handle areas of the URL namespace. For example
in test-server-v2.0.c, the URL area "/formtest" is associated with the plugin
providing "protocol-post-demo" like this
```
static const struct lws_http_mount mount_post = {
NULL, /* linked-list pointer to next*/
"/formtest", /* mountpoint in URL namespace on this vhost */
"protocol-post-demo", /* handler */
NULL, /* default filename if none given */
NULL,
0,
0,
0,
0,
0,
LWSMPRO_CALLBACK, /* origin points to a callback */
9, /* strlen("/formtest"), ie length of the mountpoint */
};
```
Client access to /formtest[anything] will be passed to the callback registered
with the named protocol, which in this case is provided by a protocol plugin.
Access by all methods, eg, GET and POST are handled by the callback.
protocol-post-demo deals with accepting and responding to the html form that
is in the test server HTML.
When a connection accesses a URL related to a CALLBACK type mount, the
connection protocol is changed until the next access on the connection to a
URL outside the same CALLBACK mount area. User space on the connection is
arranged to be the size of the new protocol user space allocation as given in
the protocol struct.
This allocation is only deleted / replaced when the connection accesses a
URL region with a different protocol (or the default protocols[0] if no
CALLBACK area matches it).

View file

@ -256,7 +256,25 @@ Other mount options
"cgi-timeout": "30"
```
3) Cache policy of the files in the mount can also be set. If no
3) `callback://` protocol may be used when defining a mount to associate a
named protocol callback with the URL namespace area. For example
```
{
"mountpoint": "/formtest",
"origin": "callback://protocol-post-demo"
}
```
All handling of client access to /formtest[anything] will be passed to the
callback registered to the protocol "protocol-post-demo".
This is useful for handling POST http body content or general non-cgi http
payload generation inside a plugin.
See the related notes in README.coding.md
4) Cache policy of the files in the mount can also be set. If no
options are given, the content is marked uncacheable.
{

View file

@ -47,6 +47,7 @@ static const char * const mount_protocols[] = {
"cgi://",
">http://",
">https://",
"callback://"
};
LWS_VISIBLE void *

View file

@ -2368,6 +2368,7 @@ lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len)
"cgi://",
">http://",
">https://",
"callback://"
};
char *orig = buf, *end = buf + len - 1, first = 1;
int n = 0;

View file

@ -1600,12 +1600,13 @@ struct lws_client_connect_info {
};
enum {
LWSMPRO_HTTP,
LWSMPRO_HTTPS,
LWSMPRO_FILE,
LWSMPRO_CGI,
LWSMPRO_REDIR_HTTP,
LWSMPRO_REDIR_HTTPS,
LWSMPRO_HTTP = 0,
LWSMPRO_HTTPS = 1,
LWSMPRO_FILE = 2,
LWSMPRO_CGI = 3,
LWSMPRO_REDIR_HTTP = 4,
LWSMPRO_REDIR_HTTPS = 5,
LWSMPRO_CALLBACK = 6,
};
LWS_EXTERN int

View file

@ -82,7 +82,7 @@ lws_header_table_reset(struct lws *wsi, int autoservice)
ah->rxlen = 0;
/* since we will restart the ah, our new headers are not completed */
wsi->hdr_parsing_completed = 0;
// wsi->hdr_parsing_completed = 0;
/*
* if we inherited pending rx (from socket adoption deferred

View file

@ -579,9 +579,10 @@ lws_http_action(struct lws *wsi)
uri_ptr[hm->mountpoint_len] == '/' ||
hm->mountpoint_len == 1)
) {
if ((hm->origin_protocol == LWSMPRO_CGI ||
if (hm->origin_protocol == LWSMPRO_CALLBACK ||
((hm->origin_protocol == LWSMPRO_CGI ||
lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI)) &&
hm->mountpoint_len > best) {
hm->mountpoint_len > best)) {
best = hm->mountpoint_len;
hit = hm;
}
@ -609,9 +610,13 @@ lws_http_action(struct lws *wsi)
* / at the end, we must redirect to add it so the browser
* understands he is one "directory level" down.
*/
if ((hit->mountpoint_len > 1 || (hit->origin_protocol & 4)) &&
(*s != '/' || (hit->origin_protocol & 4)) &&
(hit->origin_protocol != LWSMPRO_CGI)) {
if ((hit->mountpoint_len > 1 ||
(hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) &&
(*s != '/' ||
(hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) &&
(hit->origin_protocol != LWSMPRO_CGI && hit->origin_protocol != LWSMPRO_CALLBACK)) {
unsigned char *start = pt->serv_buf + LWS_PRE,
*p = start, *end = p + 512;
static const char *oprot[] = {
@ -642,6 +647,50 @@ lws_http_action(struct lws *wsi)
return lws_http_transaction_completed(wsi);
}
/*
* A particular protocol callback is mounted here?
*
* For the duration of this http transaction, bind us to the
* associated protocol
*/
if (hit->origin_protocol == LWSMPRO_CALLBACK) {
for (n = 0; n < wsi->vhost->count_protocols; n++)
if (!strcmp(wsi->vhost->protocols[n].name,
hit->origin)) {
if (wsi->protocol != &wsi->vhost->protocols[n])
if (!wsi->user_space_externally_allocated)
lws_free_set_NULL(wsi->user_space);
wsi->protocol = &wsi->vhost->protocols[n];
if (lws_ensure_user_space(wsi)) {
lwsl_err("Unable to allocate user space\n");
return 1;
}
break;
}
if (n == wsi->vhost->count_protocols) {
n = -1;
lwsl_err("Unable to find plugin '%s'\n",
hit->origin);
}
n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
wsi->user_space, uri_ptr, uri_len);
goto after;
}
/* deferred cleanup and reset to protocols[0] */
if (wsi->protocol != &wsi->vhost->protocols[0])
if (!wsi->user_space_externally_allocated)
lws_free_set_NULL(wsi->user_space);
wsi->protocol = &wsi->vhost->protocols[0];
#ifdef LWS_WITH_CGI
/* did we hit something with a cgi:// origin? */
if (hit->origin_protocol == LWSMPRO_CGI) {
@ -699,10 +748,18 @@ lws_http_action(struct lws *wsi)
n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
wsi->user_space, uri_ptr, uri_len);
}
} else
} else {
/* deferred cleanup and reset to protocols[0] */
if (wsi->protocol != &wsi->vhost->protocols[0])
if (!wsi->user_space_externally_allocated)
lws_free_set_NULL(wsi->user_space);
wsi->protocol = &wsi->vhost->protocols[0];
n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
wsi->user_space, uri_ptr, uri_len);
}
after:
if (n) {
lwsl_info("LWS_CALLBACK_HTTP closing\n");
@ -1185,6 +1242,7 @@ lws_http_transaction_completed(struct lws *wsi)
/* otherwise set ourselves up ready to go again */
wsi->state = LWSS_HTTP;
wsi->mode = LWSCM_HTTP_SERVING;
/* reset of non [0] protocols (and freeing of user_space) is deferred */
wsi->u.http.content_length = 0;
wsi->hdr_parsing_completed = 0;
#ifdef LWS_WITH_ACCESS_LOG

View file

@ -277,6 +277,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
"cgi://",
">http://",
">https://",
"callback://"
};
if (!a->m.mountpoint || !a->m.origin) {

View file

@ -0,0 +1,144 @@
/*
* ws protocol handler plugin for "dumb increment"
*
* Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
* The person who associated a work with this deed has dedicated
* the work to the public domain by waiving all of his or her rights
* to the work worldwide under copyright law, including all related
* and neighboring rights, to the extent allowed by law. You can copy,
* modify, distribute and perform the work, even for commercial purposes,
* all without asking permission.
*
* These test plugins are intended to be adapted for use in your code, which
* may be proprietary. So unlike the library itself, they are licensed
* Public Domain.
*/
#include "../lib/libwebsockets.h"
#include <string.h>
struct per_session_data__post_demo {
char post_string[256];
char result[500 + LWS_PRE];
int result_len;
};
static int
callback_post_demo(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
struct per_session_data__post_demo *pss =
(struct per_session_data__post_demo *)user;
unsigned char buffer[LWS_PRE + 512];
unsigned char *p, *start, *end;
int n;
switch (reason) {
case LWS_CALLBACK_HTTP:
lwsl_debug("LWS_CALLBACK_HTTP\n");
if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
return 0;
break;
case LWS_CALLBACK_HTTP_BODY:
lwsl_debug("LWS_CALLBACK_HTTP_BODY: len %d\n", (int)len);
strncpy(pss->post_string, in, sizeof (pss->post_string) -1);
pss->post_string[sizeof(pss->post_string) - 1] = '\0';
if (len < sizeof(pss->post_string) - 1)
pss->post_string[len] = '\0';
break;
case LWS_CALLBACK_HTTP_WRITEABLE:
lwsl_debug("LWS_CALLBACK_HTTP_WRITEABLE: sending %d\n", pss->result_len);
n = lws_write(wsi, (unsigned char *)pss->result + LWS_PRE,
pss->result_len, LWS_WRITE_HTTP);
if (n < 0)
return 1;
goto try_to_reuse;
case LWS_CALLBACK_HTTP_BODY_COMPLETION:
lwsl_debug("LWS_CALLBACK_HTTP_BODY_COMPLETION\n");
/*
* the whole of the sent body arrived,
* respond to the client with a redirect to show the
* results
*/
pss->result_len = sprintf((char *)pss->result + LWS_PRE,
"<html><body><h1>Form results</h1>'%s'<br>"
"</body></html>", pss->post_string);
p = buffer + LWS_PRE;
start = p;
end = p + sizeof(buffer) - LWS_PRE;
if (lws_add_http_header_status(wsi, 200, &p, end))
return 1;
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
(unsigned char *)"text/html", 9, &p, end))
return 1;
if (lws_add_http_header_content_length(wsi, pss->result_len, &p, end))
return 1;
if (lws_finalize_http_header(wsi, &p, end))
return 1;
n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
if (n < 0)
return 1;
/*
* send the payload next time, in case would block after
* headers
*/
lws_callback_on_writable(wsi);
break;
default:
break;
}
return 0;
try_to_reuse:
if (lws_http_transaction_completed(wsi))
return -1;
return 0;
}
static const struct lws_protocols protocols[] = {
{
"protocol-post-demo",
callback_post_demo,
sizeof(struct per_session_data__post_demo),
1024,
},
};
LWS_VISIBLE int
init_protocol_post_demo(struct lws_context *context,
struct lws_plugin_capability *c)
{
if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC,
c->api_magic);
return 1;
}
c->protocols = protocols;
c->count_protocols = ARRAY_SIZE(protocols);
c->extensions = NULL;
c->count_extensions = 0;
return 0;
}
LWS_VISIBLE int
destroy_protocol_post_demo(struct lws_context *context)
{
return 0;
}

View file

@ -69,6 +69,25 @@ static const struct lws_extension exts[] = {
{ NULL, NULL, NULL /* terminator */ }
};
/*
* mount a handler for a section of the URL space
*/
static const struct lws_http_mount mount_post = {
NULL, /* linked-list pointer to next*/
"/formtest", /* mountpoint in URL namespace on this vhost */
"protocol-post-demo", /* handler */
NULL, /* default filename if none given */
NULL,
0,
0,
0,
0,
0,
LWSMPRO_CALLBACK, /* origin points to a callback */
9, /* strlen("/formtest"), ie length of the mountpoint */
};
/*
* mount a filesystem directory into the URL space at /
* point it to our /usr/share directory with our assets in
@ -76,7 +95,7 @@ static const struct lws_extension exts[] = {
*/
static const struct lws_http_mount mount = {
NULL, /* linked-list pointer to next, but we only have one */
(struct lws_http_mount *)&mount_post, /* linked-list pointer to next*/
"/", /* mountpoint in URL namespace on this vhost */
LOCAL_RESOURCE_PATH, /* where to go on the filesystem for that */
"test.html", /* default filename if none given */
@ -108,9 +127,16 @@ static const struct lws_protocol_vhost_options pvo_opt = {
* linked-list. We can also give the plugin per-vhost options here.
*/
static const struct lws_protocol_vhost_options pvo_2 = {
static const struct lws_protocol_vhost_options pvo_3 = {
NULL,
NULL,
"protocol-post-demo",
"" /* ignored, just matches the protocol name above */
};
static const struct lws_protocol_vhost_options pvo_2 = {
&pvo_3,
NULL,
"lws-status",
"" /* ignored, just matches the protocol name above */
};
@ -160,7 +186,6 @@ static const struct option options[] = {
{ "ssl-crl", required_argument, NULL, 'R' },
#endif
#endif
{ "libev", no_argument, NULL, 'e' },
#ifndef LWS_NO_DAEMONIZE
{ "daemonize", no_argument, NULL, 'D' },
#endif
@ -200,13 +225,10 @@ int main(int argc, char **argv)
info.port = 7681;
while (n >= 0) {
n = getopt_long(argc, argv, "ei:hsap:d:Dr:C:K:A:R:vu:g:", options, NULL);
n = getopt_long(argc, argv, "i:hsap:d:Dr:C:K:A:R:vu:g:", options, NULL);
if (n < 0)
continue;
switch (n) {
case 'e':
opts |= LWS_SERVER_OPTION_LIBEV;
break;
#ifndef LWS_NO_DAEMONIZE
case 'D':
daemonize = 1;