"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")
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).
"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.
{
"cgi://",
">http://",
">https://",
+ "callback://"
};
LWS_VISIBLE void *
"cgi://",
">http://",
">https://",
+ "callback://"
};
char *orig = buf, *end = buf + len - 1, first = 1;
int n = 0;
};
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
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
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;
}
* / 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[] = {
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) {
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");
/* 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
"cgi://",
">http://",
">https://",
+ "callback://"
};
if (!a->m.mountpoint || !a->m.origin) {
--- /dev/null
+/*
+ * 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;
+}
};
/*
+ * 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
* stuff from here is autoserved by the library
*/
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 */
* 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 */
};
{ "ssl-crl", required_argument, NULL, 'R' },
#endif
#endif
- { "libev", no_argument, NULL, 'e' },
#ifndef LWS_NO_DAEMONIZE
{ "daemonize", no_argument, NULL, 'D' },
#endif
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;