Skip to content

Commit fdd75a8

Browse files
committed
dist/tools/coap-yolo: Add WebSocket2UDP proxy
This proxy allows using CoAP over YOLO with regular CoAP over WebSocket clients by forwarding binary messages received via WebSocket to a given UDP endpoint and the replies back to the WebSocket.
1 parent 50ee56b commit fdd75a8

File tree

2 files changed

+116
-0
lines changed

2 files changed

+116
-0
lines changed

dist/tools/coap-yolo/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
CoAP over YOLO Utils
2+
====================
3+
4+
CoAP over YOLO is using the CoAP over WebSocket serialization but sending the
5+
messages over UDP. The CoAP over WebSocket format depends on a reliable,
6+
order-preserving, duplication-free message transport; which UDP clearly is not.
7+
Hence the name YOLO.
8+
9+
However, if the RIOT note is connected to a Linux host with a single reliable,
10+
order-preserving and duplication-free link, this should work.
11+
12+
This folder contains WebSocket to UDP proxy that forwards messages received
13+
on the WebSocket to a UDP endpoint specified by the command line, and forwards
14+
any replies received via UDP back to the WebSocket. This allows
15+
exposing a CoAP over YOLO as a CoAP over WebSocket. With this you can use any
16+
CoAP over WebSocket implementation such as e.g. `coap-client` from
17+
[libcoap](https://libcoap.net/) to connect to CoAP over YOLO.
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#!/usr/bin/python3
2+
"""
3+
Bridge that translates CoAP over YOLO to CoAP over WebSocket.
4+
"""
5+
6+
import aiohttp
7+
import aiohttp.web
8+
import argparse
9+
import asyncio
10+
import sys
11+
12+
udp_ep = None
13+
udp_transport = None
14+
ws = None
15+
16+
class ForwardFromUdpProtocol:
17+
"""
18+
Forward received UDP datagrams via the currently connected WebSocket
19+
"""
20+
def connection_made(self, transport):
21+
pass
22+
23+
def datagram_received(self, data, addr):
24+
global ws
25+
if ws is not None:
26+
asyncio.ensure_future(ws.send_bytes(data), loop=asyncio.get_event_loop())
27+
28+
29+
async def websocket_handler(request):
30+
"""
31+
Forward received WebSocket messages to the (statically) configured UDP
32+
destination endpoint
33+
"""
34+
global udp_transport
35+
global udp_ep
36+
global ws
37+
if ws is not None:
38+
print("Someone already is connected")
39+
return
40+
ws = aiohttp.web.WebSocketResponse(protocols=("coap"))
41+
print("WebSocket connection opened")
42+
await ws.prepare(request)
43+
44+
async for msg in ws:
45+
if msg.type == aiohttp.WSMsgType.BINARY:
46+
udp_transport.sendto(msg.data, udp_ep)
47+
elif msg.type == aiohttp.WSMsgType.CLOSED:
48+
udp_transport.sendto(b'', udp_ep)
49+
ws = None
50+
return
51+
else:
52+
print(f"Warning: Got unexpected WebSocket Message {msg}")
53+
54+
udp_transport.sendto(b'', udp_ep)
55+
ws = None
56+
print("WebSocket connection closed")
57+
58+
59+
async def ws2yolo(_udp_ep, ws_ep, udp_local_ep):
60+
"""
61+
Run a WebSocket 2 CoAP over YOLO bridge with the given endpoints
62+
"""
63+
global udp_transport
64+
global udp_ep
65+
udp_ep = _udp_ep
66+
loop = asyncio.get_running_loop()
67+
udp_transport, protocol = await loop.create_datagram_endpoint(
68+
ForwardFromUdpProtocol,
69+
local_addr=udp_local_ep)
70+
71+
app = aiohttp.web.Application()
72+
app.router.add_route('GET', '/.well-known/coap', websocket_handler)
73+
runner = aiohttp.web.AppRunner(app)
74+
await runner.setup()
75+
site = aiohttp.web.TCPSite(runner)
76+
await site.start()
77+
await asyncio.Event().wait()
78+
79+
80+
if __name__ == "__main__":
81+
DESCRIPTION = "Forward WebSocket messages via UDP"
82+
parser = argparse.ArgumentParser(description=DESCRIPTION)
83+
parser.add_argument("--udp-host", default="::1", type=str,
84+
help="UDP host to forward to")
85+
parser.add_argument("--udp-port", default=1337, type=int,
86+
help="UDP port to forward to")
87+
parser.add_argument("--local-host", default=None, type=str,
88+
help="UDP host to forward from")
89+
parser.add_argument("--local-port", default=0, type=int,
90+
help="UDP port to forward from")
91+
parser.add_argument("--ws-host", default="::1", type=str,
92+
help="WebSocket host to listen at")
93+
parser.add_argument("--ws-port", default=8080, type=int,
94+
help="WebSocket port to listen at")
95+
96+
args = parser.parse_args()
97+
asyncio.run(ws2yolo((args.udp_host, args.udp_port),
98+
(args.ws_host, args.ws_port),
99+
(args.local_host, args.local_port)))

0 commit comments

Comments
 (0)