summaryrefslogtreecommitdiff
path: root/libs/surfaces
diff options
context:
space:
mode:
authorLuciano Iam <lucianito@gmail.com>2020-04-09 15:57:09 +0200
committerRobin Gareus <robin@gareus.org>2020-04-09 20:56:46 +0200
commit40520a6dc61de135c09cdfb0097910b766ccb6ee (patch)
treebff15d687819ebdc43cc19219ad5ecba40b40771 /libs/surfaces
parent3da0cf2a31ddeac41d560d15230f8f99335b802e (diff)
Clean up and prepare for HTTP
Diffstat (limited to 'libs/surfaces')
-rw-r--r--libs/surfaces/websockets/ardour_websockets.cc2
-rw-r--r--libs/surfaces/websockets/ardour_websockets.h6
-rw-r--r--libs/surfaces/websockets/interface.cc4
-rw-r--r--libs/surfaces/websockets/server.cc185
-rw-r--r--libs/surfaces/websockets/server.h22
5 files changed, 163 insertions, 56 deletions
diff --git a/libs/surfaces/websockets/ardour_websockets.cc b/libs/surfaces/websockets/ardour_websockets.cc
index 38c7849839..2f2e9aff9c 100644
--- a/libs/surfaces/websockets/ardour_websockets.cc
+++ b/libs/surfaces/websockets/ardour_websockets.cc
@@ -35,7 +35,7 @@ using namespace ArdourSurface;
#include "pbd/abstract_ui.cc" // instantiate template
ArdourWebsockets::ArdourWebsockets (Session& s)
- : ControlProtocol (s, X_ (SURFACE_NAME))
+ : ControlProtocol (s, X_ (surface_name))
, AbstractUI<ArdourWebsocketsUIRequest> (name ())
, _strips (*this)
, _globals (*this)
diff --git a/libs/surfaces/websockets/ardour_websockets.h b/libs/surfaces/websockets/ardour_websockets.h
index 0b61032e80..21d444183a 100644
--- a/libs/surfaces/websockets/ardour_websockets.h
+++ b/libs/surfaces/websockets/ardour_websockets.h
@@ -39,11 +39,11 @@
#include "server.h"
#include "strips.h"
-#define SURFACE_NAME "WebSockets Server (Experimental)"
-#define SURFACE_ID "uri://ardour.org/surfaces/ardour_websockets:0"
-
namespace ArdourSurface
{
+const char * const surface_name = "WebSockets Server (Experimental)";
+const char * const surface_id = "uri://ardour.org/surfaces/ardour_websockets:0";
+
struct ArdourWebsocketsUIRequest : public BaseUI::BaseRequestObject {
public:
ArdourWebsocketsUIRequest () {}
diff --git a/libs/surfaces/websockets/interface.cc b/libs/surfaces/websockets/interface.cc
index 462ac79cb8..fc426ac56b 100644
--- a/libs/surfaces/websockets/interface.cc
+++ b/libs/surfaces/websockets/interface.cc
@@ -56,8 +56,8 @@ ardour_websockets_request_buffer_factory (uint32_t num_requests)
}
static ControlProtocolDescriptor ardour_websockets_descriptor = {
- /*name : */ SURFACE_NAME,
- /*id : */ SURFACE_ID,
+ /*name : */ surface_name,
+ /*id : */ surface_id,
/*ptr : */ 0,
/*module : */ 0,
/*mandatory : */ 0,
diff --git a/libs/surfaces/websockets/server.cc b/libs/surfaces/websockets/server.cc
index beab8fb254..a64d42c00b 100644
--- a/libs/surfaces/websockets/server.cc
+++ b/libs/surfaces/websockets/server.cc
@@ -38,6 +38,8 @@
#endif
#endif
+#define MAX_INDEX_SIZE 65536
+
using namespace Glib;
WebsocketsServer::WebsocketsServer (ArdourSurface::ArdourWebsockets& surface)
@@ -49,7 +51,6 @@ WebsocketsServer::WebsocketsServer (ArdourSurface::ArdourWebsockets& surface)
memset (&proto, 0, sizeof (lws_protocols));
proto.name = "lws-ardour";
proto.callback = WebsocketsServer::lws_callback;
- proto.per_session_data_size = 0;
proto.rx_buffer_size = 0;
proto.id = 0;
proto.user = 0;
@@ -59,9 +60,29 @@ WebsocketsServer::WebsocketsServer (ArdourSurface::ArdourWebsockets& surface)
_lws_proto[0] = proto;
memset (&_lws_proto[1], 0, sizeof (lws_protocols));
+ // '/' is served by a static index.html file in the surface data directory
+ // inside it there is a 'builtin' subdirectory that contains all built-in
+ // surfaces so there is no need to create a dedicated mount point for them
+ // list of surfaces is available as a dynamically generated json file
+ memset (&_lws_mnt_index, 0, sizeof (lws_http_mount));
+ _lws_mnt_index.mountpoint = "/";
+ _lws_mnt_index.mountpoint_len = strlen (_lws_mnt_index.mountpoint);
+ _lws_mnt_index.origin_protocol = LWSMPRO_FILE;
+ _lws_mnt_index.origin = _resources.index_dir ().c_str ();
+
+ // user defined surfaces in the user config directory
+ memset (&_lws_mnt_user, 0, sizeof (lws_http_mount));
+ _lws_mnt_user.mountpoint = "/user";
+ _lws_mnt_user.mountpoint_len = strlen (_lws_mnt_user.mountpoint);
+ _lws_mnt_user.origin_protocol = LWSMPRO_FILE;
+ _lws_mnt_user.origin = _resources.user_dir ().c_str ();
+
+ _lws_mnt_index.mount_next = &_lws_mnt_user;
+
memset (&_lws_info, 0, sizeof (lws_context_creation_info));
_lws_info.port = WEBSOCKET_LISTEN_PORT;
_lws_info.protocols = _lws_proto;
+ _lws_info.mounts = &_lws_mnt_index;
_lws_info.uid = -1;
_lws_info.gid = -1;
_lws_info.user = this;
@@ -141,7 +162,7 @@ WebsocketsServer::update_all_clients (const NodeState& state, bool force)
}
}
-void
+int
WebsocketsServer::add_poll_fd (struct lws_pollargs* pa)
{
/* fd can be SOCKET or int depending platform */
@@ -168,14 +189,16 @@ WebsocketsServer::add_poll_fd (struct lws_pollargs* pa)
ctx.wg_iosrc = Glib::RefPtr<Glib::IOSource> (0);
_fd_ctx[fd] = ctx;
+
+ return 0;
}
-void
+int
WebsocketsServer::mod_poll_fd (struct lws_pollargs* pa)
{
LwsPollFdGlibSourceMap::iterator it = _fd_ctx.find (pa->fd);
if (it == _fd_ctx.end ()) {
- return;
+ return 1;
}
it->second.lws_pfd.events = pa->events;
@@ -189,7 +212,7 @@ WebsocketsServer::mod_poll_fd (struct lws_pollargs* pa)
if (it->second.wg_iosrc) {
/* already polling for write */
- return;
+ return 0;
}
RefPtr<IOSource> wg_iosrc = it->second.g_channel->create_watch (Glib::IO_OUT);
@@ -202,14 +225,16 @@ WebsocketsServer::mod_poll_fd (struct lws_pollargs* pa)
it->second.wg_iosrc = Glib::RefPtr<Glib::IOSource> (0);
}
}
+
+ return 0;
}
-void
+int
WebsocketsServer::del_poll_fd (struct lws_pollargs* pa)
{
LwsPollFdGlibSourceMap::iterator it = _fd_ctx.find (pa->fd);
if (it == _fd_ctx.end ()) {
- return;
+ return 1;
}
it->second.rg_iosrc->destroy ();
@@ -219,30 +244,36 @@ WebsocketsServer::del_poll_fd (struct lws_pollargs* pa)
}
_fd_ctx.erase (it);
+
+ return 0;
}
-void
+int
WebsocketsServer::add_client (Client wsi)
{
_client_ctx.emplace (wsi, ClientContext (wsi));
dispatcher ().update_all_nodes (wsi); // send all state
+ return 0;
}
-void
+int
WebsocketsServer::del_client (Client wsi)
{
ClientContextMap::iterator it = _client_ctx.find (wsi);
+
if (it != _client_ctx.end ()) {
_client_ctx.erase (it);
}
+
+ return 0;
}
-void
+int
WebsocketsServer::recv_client (Client wsi, void* buf, size_t len)
{
NodeStateMessage msg (buf, len);
if (!msg.is_valid ()) {
- return;
+ return 1;
}
#ifndef NDEBUG
@@ -251,26 +282,28 @@ WebsocketsServer::recv_client (Client wsi, void* buf, size_t len)
ClientContextMap::iterator it = _client_ctx.find (wsi);
if (it == _client_ctx.end ()) {
- return;
+ return 1;
}
/* avoid echo */
it->second.update_state (msg.state ());
dispatcher ().dispatch (wsi, msg);
+
+ return 0;
}
-void
+int
WebsocketsServer::write_client (Client wsi)
{
ClientContextMap::iterator it = _client_ctx.find (wsi);
if (it == _client_ctx.end ()) {
- return;
+ return 1;
}
ClientOutputBuffer& pending = it->second.output_buf ();
if (pending.empty ()) {
- return;
+ return 0;
}
/* one lws_write() call per LWS_CALLBACK_SERVER_WRITEABLE callback */
@@ -279,13 +312,15 @@ WebsocketsServer::write_client (Client wsi)
pending.pop_front ();
unsigned char out_buf[1024];
- size_t len = msg.serialize (out_buf + LWS_PRE, 1024 - LWS_PRE);
+ int len = msg.serialize (out_buf + LWS_PRE, 1024 - LWS_PRE);
if (len > 0) {
#ifndef NDEBUG
std::cerr << "TX " << msg.state ().debug_str () << std::endl;
#endif
- lws_write (wsi, out_buf + LWS_PRE, len, LWS_WRITE_TEXT);
+ if (lws_write (wsi, out_buf + LWS_PRE, len, LWS_WRITE_TEXT) != len) {
+ return 1;
+ }
} else {
PBD::error << "ArdourWebsockets: cannot serialize message" << endmsg;
}
@@ -293,21 +328,84 @@ WebsocketsServer::write_client (Client wsi)
if (!pending.empty ()) {
lws_callback_on_writable (wsi);
}
+
+ return 0;
}
-void
-WebsocketsServer::reject_http_client (Client wsi)
+int
+WebsocketsServer::send_index_hdr (Client wsi)
+{
+ char url[1024];
+
+ if (lws_hdr_copy (wsi, url, 1024, WSI_TOKEN_GET_URI) < 0) {
+ return 1;
+ }
+
+ if (strcmp (url, "/index.json") != 0) {
+ lws_return_http_status (wsi, 404, 0);
+ return 1;
+ }
+
+ unsigned char out_buf[1024],
+ *start = out_buf,
+ *p = start,
+ *end = &out_buf[sizeof(out_buf) - 1];
+
+#if LWS_LIBRARY_VERSION_MAJOR >= 3
+ lws_add_http_common_headers (wsi, HTTP_STATUS_OK, "application/json",
+ LWS_ILLEGAL_HTTP_CONTENT_LEN, &p, end);
+ lws_add_http_header_by_token (wsi, WSI_TOKEN_HTTP_CACHE_CONTROL,
+ reinterpret_cast<const unsigned char*> ("no-store"), 8, &p, end);
+
+ if (lws_finalize_write_http_header (wsi, start, &p, end) != 0) {
+ return 1;
+ }
+#else
+ lws_add_http_header_status (wsi, HTTP_STATUS_OK, &p, end);
+ lws_add_http_header_by_token (wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
+ reinterpret_cast<const unsigned char*> ("application/json"), 16, &p, end);
+ lws_add_http_header_by_token (wsi, WSI_TOKEN_CONNECTION,
+ reinterpret_cast<const unsigned char*> ("close"), 5, &p, end);
+ lws_add_http_header_by_token (wsi, WSI_TOKEN_HTTP_CACHE_CONTROL,
+ reinterpret_cast<const unsigned char*> ("no-store"), 8, &p, end);
+ lws_finalize_http_header (wsi, &p, end);
+
+ int len = p - start;
+
+ if (lws_write (wsi, start, len, LWS_WRITE_HTTP_HEADERS) != len) {
+ return 1;
+ }
+#endif
+
+ lws_callback_on_writable (wsi);
+
+ return 0;
+}
+
+int
+WebsocketsServer::send_index_body (Client wsi)
{
- const char *html_body = "<p>This URL is not meant to be accessed via HTTP; for example using"
- " a web browser. Refer to Ardour documentation for further information.</p>";
- lws_return_http_status (wsi, 404, html_body);
+ std::string index = _resources.scan ();
+
+ char body[MAX_INDEX_SIZE];
+ //lws_strncpy (body, index.c_str (), sizeof(body));
+ memset (body, 0, sizeof (body));
+ strncpy (body, index.c_str (), sizeof(body) - 1);
+ int len = strlen (body);
+
+ if (lws_write (wsi, reinterpret_cast<unsigned char*> (body), len, LWS_WRITE_HTTP) != len) {
+ return 1;
+ }
+
+ lws_http_transaction_completed (wsi);
+
+ return -1; // end connection
}
bool
WebsocketsServer::io_handler (Glib::IOCondition ioc, lws_sockfd_type fd)
{
- /* IO_IN=1, IO_PRI=2, IO_ERR=8, IO_HUP=16 */
- //printf ("io_handler ioc = %d\n", ioc);
+ /* IO_IN=1, IO_PRI=2, IO_OUT=4, IO_ERR=8, IO_HUP=16 */
LwsPollFdGlibSourceMap::iterator it = _fd_ctx.find (fd);
if (it == _fd_ctx.end ()) {
@@ -317,9 +415,7 @@ WebsocketsServer::io_handler (Glib::IOCondition ioc, lws_sockfd_type fd)
struct lws_pollfd* lws_pfd = &it->second.lws_pfd;
lws_pfd->revents = ioc_to_events (ioc);
- if (lws_service_fd (_lws_context, lws_pfd) < 0) {
- return false;
- }
+ lws_service_fd (_lws_context, lws_pfd);
return ioc & (Glib::IO_IN | Glib::IO_OUT);
}
@@ -368,41 +464,48 @@ WebsocketsServer::lws_callback (struct lws* wsi, enum lws_callback_reasons reaso
{
void* ctx_userdata = lws_context_user (lws_get_context (wsi));
WebsocketsServer* server = static_cast<WebsocketsServer*> (ctx_userdata);
+ int rc;
switch (reason) {
case LWS_CALLBACK_ADD_POLL_FD:
- server->add_poll_fd (static_cast<struct lws_pollargs*> (in));
+ rc = server->add_poll_fd (static_cast<struct lws_pollargs*> (in));
break;
case LWS_CALLBACK_CHANGE_MODE_POLL_FD:
- server->mod_poll_fd (static_cast<struct lws_pollargs*> (in));
+ rc = server->mod_poll_fd (static_cast<struct lws_pollargs*> (in));
break;
case LWS_CALLBACK_DEL_POLL_FD:
- server->del_poll_fd (static_cast<struct lws_pollargs*> (in));
+ rc = server->del_poll_fd (static_cast<struct lws_pollargs*> (in));
break;
case LWS_CALLBACK_ESTABLISHED:
- server->add_client (wsi);
+ rc = server->add_client (wsi);
break;
case LWS_CALLBACK_CLOSED:
- server->del_client (wsi);
+ rc = server->del_client (wsi);
break;
case LWS_CALLBACK_RECEIVE:
- server->recv_client (wsi, in, len);
+ rc = server->recv_client (wsi, in, len);
break;
case LWS_CALLBACK_SERVER_WRITEABLE:
- server->write_client (wsi);
+ rc = server->write_client (wsi);
break;
+ /* will be called only if the requested url is not fulfilled
+ by the any of the mount configurations (index, builtin, user) */
case LWS_CALLBACK_HTTP:
- server->reject_http_client (wsi);
- return 1;
+ rc = server->send_index_hdr (wsi);
break;
+ case LWS_CALLBACK_HTTP_WRITEABLE:
+ rc = server->send_index_body (wsi);
+ break;
+
+ case LWS_CALLBACK_CLOSED_HTTP:
case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
case LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED:
@@ -421,20 +524,18 @@ WebsocketsServer::lws_callback (struct lws* wsi, enum lws_callback_reasons reaso
case LWS_CALLBACK_HTTP_CONFIRM_UPGRADE:
#endif
#endif
+ /* do nothing but keep connection alive */
+ rc = 0;
break;
- /* TODO: handle HTTP connections.
- * Serve static ctrl-surface pages, JS, CSS etc.
- */
-
default:
#ifndef NDEBUG
/* see libwebsockets.h lws_callback_reasons */
std::cerr << "LWS: unhandled callback " << reason << std::endl;
#endif
- return -1;
+ rc = -1;
break;
}
- return 0;
+ return rc;
}
diff --git a/libs/surfaces/websockets/server.h b/libs/surfaces/websockets/server.h
index c585f22655..a47fbee51c 100644
--- a/libs/surfaces/websockets/server.h
+++ b/libs/surfaces/websockets/server.h
@@ -38,6 +38,7 @@
#include "component.h"
#include "message.h"
#include "state.h"
+#include "resources.h"
#define WEBSOCKET_LISTEN_PORT 9000
@@ -62,6 +63,8 @@ public:
private:
struct lws_protocols _lws_proto[2];
+ struct lws_http_mount _lws_mnt_index;
+ struct lws_http_mount _lws_mnt_user;
struct lws_context_creation_info _lws_info;
struct lws_context* _lws_context;
@@ -73,15 +76,18 @@ private:
typedef boost::unordered_map<Client, ClientContext> ClientContextMap;
ClientContextMap _client_ctx;
- void add_poll_fd (struct lws_pollargs*);
- void mod_poll_fd (struct lws_pollargs*);
- void del_poll_fd (struct lws_pollargs*);
+ ServerResources _resources;
- void add_client (Client);
- void del_client (Client);
- void recv_client (Client, void* buf, size_t len);
- void write_client (Client);
- void reject_http_client (Client);
+ int add_poll_fd (struct lws_pollargs*);
+ int mod_poll_fd (struct lws_pollargs*);
+ int del_poll_fd (struct lws_pollargs*);
+
+ int add_client (Client);
+ int del_client (Client);
+ int recv_client (Client, void*, size_t);
+ int write_client (Client);
+ int send_index_hdr (Client);
+ int send_index_body (Client);
bool io_handler (Glib::IOCondition, lws_sockfd_type);