Browse Source

MT#59038 add support for "plain" NG protocols

For convenience we provide extra HTTP and WS endpoints that accept a
cookie-less NG or JSON message string. Not all commands are sensitive to
retransmits and this makes it easier to query call status etc.

Change-Id: Iffbc4ef9a5fdf916a374dfdd4042c61b437d18c9
pull/1802/head
Richard Fuchs 2 years ago
parent
commit
72b388a7ce
5 changed files with 103 additions and 12 deletions
  1. +13
    -0
      daemon/control_ng.c
  2. +43
    -9
      daemon/websocket.c
  3. +11
    -3
      docs/http_websocket_support.md
  4. +2
    -0
      include/control_ng.h
  5. +34
    -0
      t/auto-daemon-tests-websocket.py

+ 13
- 0
daemon/control_ng.c View File

@ -421,6 +421,19 @@ int control_ng_process(str *buf, const endpoint_t *sin, char *addr, const sockad
return 0;
}
int control_ng_process_plain(str *data, const endpoint_t *sin, char *addr, const sockaddr_t *local,
void (*cb)(str *, str *, const endpoint_t *, const sockaddr_t *, void *),
void *p1, struct obj *ref)
{
g_autoptr(ng_buffer) ngbuf = NULL;
str reply;
control_ng_process_payload(&reply, data, sin, addr, ref, &ngbuf);
cb(NULL, &reply, sin, local, p1);
return 0;
}
INLINE void control_ng_send_generic(str *cookie, str *body, const endpoint_t *sin, const sockaddr_t *from,
void *p1)
{


+ 43
- 9
daemon/websocket.c View File

@ -474,8 +474,10 @@ static void websocket_ng_send_ws(str *cookie, str *body, const endpoint_t *sin,
void *p1)
{
struct websocket_conn *wc = p1;
websocket_queue_raw(wc, cookie->s, cookie->len);
websocket_queue_raw(wc, " ", 1);
if (cookie) {
websocket_queue_raw(wc, cookie->s, cookie->len);
websocket_queue_raw(wc, " ", 1);
}
websocket_queue_raw(wc, body->s, body->len);
websocket_write_binary(wc, NULL, 0, true);
}
@ -483,9 +485,12 @@ static void websocket_ng_send_http(str *cookie, str *body, const endpoint_t *sin
void *p1)
{
struct websocket_conn *wc = p1;
websocket_http_response(wc, 200, "application/x-rtpengine-ng", cookie->len + 1 + body->len);
websocket_queue_raw(wc, cookie->s, cookie->len);
websocket_queue_raw(wc, " ", 1);
websocket_http_response(wc, 200, "application/x-rtpengine-ng",
(cookie ? (cookie->len + 1) : 0) + body->len);
if (cookie) {
websocket_queue_raw(wc, cookie->s, cookie->len);
websocket_queue_raw(wc, " ", 1);
}
websocket_queue_raw(wc, body->s, body->len);
websocket_write_http(wc, NULL, true);
}
@ -495,7 +500,9 @@ static void __ng_buf_free(void *p) {
g_string_free(buf->body, TRUE);
}
static const char *websocket_ng_process(struct websocket_message *wm) {
static const char *websocket_ng_process_generic(struct websocket_message *wm,
__typeof__(control_ng_process) cb)
{
struct websocket_ng_buf *buf = obj_alloc0("websocket_ng_buf", sizeof(*buf), __ng_buf_free);
endpoint_print(&wm->wc->endpoint, buf->addr, sizeof(buf->addr));
@ -508,13 +515,21 @@ static const char *websocket_ng_process(struct websocket_message *wm) {
str_init_len(&buf->cmd, buf->body->str, buf->body->len);
buf->endpoint = wm->wc->endpoint;
control_ng_process(&buf->cmd, &buf->endpoint, buf->addr, NULL, websocket_ng_send_ws, wm->wc, &buf->obj);
cb(&buf->cmd, &buf->endpoint, buf->addr, NULL, websocket_ng_send_ws, wm->wc, &buf->obj);
obj_put(buf);
return NULL;
}
static const char *websocket_http_ng(struct websocket_message *wm) {
static const char *websocket_ng_process(struct websocket_message *wm) {
return websocket_ng_process_generic(wm, control_ng_process);
}
static const char *websocket_ng_plain_process(struct websocket_message *wm) {
return websocket_ng_process_generic(wm, control_ng_process_plain);
}
static const char *websocket_http_ng_generic(struct websocket_message *wm,
__typeof__(control_ng_process) cb)
{
struct websocket_ng_buf *buf = obj_alloc0("websocket_ng_buf", sizeof(*buf), __ng_buf_free);
endpoint_print(&wm->wc->endpoint, buf->addr, sizeof(buf->addr));
@ -527,7 +542,7 @@ static const char *websocket_http_ng(struct websocket_message *wm) {
str_init_len(&buf->cmd, buf->body->str, buf->body->len);
buf->endpoint = wm->wc->endpoint;
if (control_ng_process(&buf->cmd, &buf->endpoint, buf->addr, NULL, websocket_ng_send_http, wm->wc,
if (cb(&buf->cmd, &buf->endpoint, buf->addr, NULL, websocket_ng_send_http, wm->wc,
&buf->obj))
websocket_http_complete(wm->wc, 600, "text/plain", 6, "error\n");
@ -535,6 +550,12 @@ static const char *websocket_http_ng(struct websocket_message *wm) {
return NULL;
}
static const char *websocket_http_ng(struct websocket_message *wm) {
return websocket_http_ng_generic(wm, control_ng_process);
}
static const char *websocket_http_ng_plain(struct websocket_message *wm) {
return websocket_http_ng_generic(wm, control_ng_process_plain);
}
@ -645,6 +666,9 @@ static int websocket_http_body(struct websocket_conn *wc, const char *body, size
if (!strcmp(uri, "/ng") && wm->method == M_POST && wm->content_type == CT_NG)
handler = websocket_http_ng;
if (!strcmp(uri, "/ng-plain") && wm->method == M_POST
&& (wm->content_type == CT_NG || wm->content_type == CT_JSON))
handler = websocket_http_ng_plain;
else if (!strcmp(uri, "/admin") && wm->method == M_POST && wm->content_type == CT_JSON)
handler = websocket_janus_process;
else if (!strcmp(uri, "/janus") && wm->method == M_POST && wm->content_type == CT_JSON)
@ -945,6 +969,11 @@ static int websocket_rtpengine_ng(struct lws *wsi, enum lws_callback_reasons rea
{
return websocket_protocol(wsi, reason, user, in, len, websocket_ng_process, "rtpengine-ng");
}
static int websocket_rtpengine_ng_plain(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in,
size_t len)
{
return websocket_protocol(wsi, reason, user, in, len, websocket_ng_plain_process, "rtpengine-ng-plain");
}
static const struct lws_protocols websocket_protocols[] = {
@ -973,6 +1002,11 @@ static const struct lws_protocols websocket_protocols[] = {
.callback = websocket_rtpengine_ng,
.per_session_data_size = sizeof(struct websocket_conn),
},
{
.name = "ng-plain.rtpengine.com",
.callback = websocket_rtpengine_ng_plain,
.per_session_data_size = sizeof(struct websocket_conn),
},
{ 0, }
};


+ 11
- 3
docs/http_websocket_support.md View File

@ -31,12 +31,20 @@ For HTTP and HTTPS, the URI `/ng` is used, with the request being made by
`POST` and the content-type set to `application/x-rtpengine-ng`. The message
body must be in the same format as the body of an UDP-based *ng* message and
must therefore consist of a unique cookie string, followed by a single space,
followed by the message in *bencode* format. Likewise, the response will be in
the same format, including the unique cookie.
followed by the message in *bencode* format or *JSON* format. Likewise, the
response will be in the same format, including the unique cookie.
For WebSockets, the subprotocol `ng.rtpengine.com` is used and the protocol
follows the same format. Messages must consist of a unique cookie and a string
in bencode format, and responses will also be in the same format.
in *bencode* format or *JSON* format, and responses will also be in the same
format.
Additionally the URI `/ng-plain` and the WebSocket subprotocol
`ng-plain.rtpengine.com` are supported, which operate identical to what is
described above except that they carry *ng* protocol messages without the
unique cookie. In other words, each payload is just a plain *bencode*
dictionary or a *JSON* object. Therefore the content-type `application/json`
can also be used for HTTP `POST`.
## Prometheus Stats Exporter


+ 2
- 0
include/control_ng.h View File

@ -74,6 +74,8 @@ void control_ng_init(void);
void control_ng_cleanup(void);
int control_ng_process(str *buf, const endpoint_t *sin, char *addr, const sockaddr_t *local,
void (*cb)(str *, str *, const endpoint_t *, const sockaddr_t *, void *), void *p1, struct obj *);
int control_ng_process_plain(str *buf, const endpoint_t *sin, char *addr, const sockaddr_t *local,
void (*cb)(str *, str *, const endpoint_t *, const sockaddr_t *, void *), void *p1, struct obj *);
ng_buffer *ng_buffer_new(struct obj *ref);


+ 34
- 0
t/auto-daemon-tests-websocket.py View File

@ -132,6 +132,40 @@ class TestWSCli(unittest.TestCase):
)
class TestNGPlain(unittest.TestCase):
@classmethod
def setUpClass(cls):
eventloop.run_until_complete(get_ws(cls, "ng-plain.rtpengine.com"))
@classmethod
def tearDownClass(cls):
eventloop.run_until_complete(close_ws(cls))
def testPing(self):
eventloop.run_until_complete(testIO(self, "d7:command4:pinge"))
self.assertEqual(
self._res,
b"d6:result4:ponge",
)
class TestNGPlainJSON(unittest.TestCase):
@classmethod
def setUpClass(cls):
eventloop.run_until_complete(get_ws(cls, "ng-plain.rtpengine.com"))
@classmethod
def tearDownClass(cls):
eventloop.run_until_complete(close_ws(cls))
def testPing(self):
eventloop.run_until_complete(testIOJson(self, {"command": "ping"}))
self.assertEqual(
self._res,
{"result": "pong"},
)
class TestWSJanus(unittest.TestCase):
@classmethod
def setUpClass(cls):


Loading…
Cancel
Save