2 undergr0und
Philip Trauner edited this page 2017-02-09 18:23:55 +01:00

undergr0und is a wrapper around ws4py that is needed for routing and exchanging routes automatically. Some light modification (~3 lines) were made to ws4py itself to allow for server-side handler kwargs.

There is also a JavaScript implementation that was required for dashb0ard: undergr0und.js

Name

Highway, undergr0und, ... what is going on here?

Highway was renamed to undergr0und during development to be more in line with the other fl0w components.

Why

fl0w requires a very specialised networking solution in order to remain as modular as possible while still being reasonably high level. That's why every single component of fl0w can implement its own server/client network-subprotocol which is then "routed through a pipe" to the same component on the other side. undergr0und also utilises this mechanic to manage itself. There is no explicit receive call, the network protocol is completely asynchronous.

Example

Client

from undergr0und import Client as BaseClient
from undergr0und import Route

class Info(Route):
	def run(self, data, handler):
		print(data)

class Client(BaseClient):
	def ready(self):
		self.send(None, "info")

try:
	ws = Client('ws://127.0.0.1:3077')
	# setup has to be called before the connection is established
	ws.setup({"info" : Info()}, debug=True)
	ws.connect()
	ws.run_forever()
except KeyboardInterrupt:
	ws.close()

Server

from wsgiref.simple_server import make_server
from ws4py.server.wsgirefserver import WSGIServer, WebSocketWSGIRequestHandler
from ws4py.server.wsgiutils import WebSocketWSGIApplication

from undergr0und import Server as BaseServer
from undergr0und import Route

from time import time

class Info(Route):
	def run(self, data, handler):
		handler.send(time(), handler.reverse_routes[self])


class Server(BaseServer):
	pass


server = make_server("127.0.0.1", 3077, 
	server_class=WSGIServer, handler_class=WebSocketWSGIRequestHandler, 
	app=WebSocketWSGIApplication(handler_cls=Server, 
		handler_args={"routes" : {"info" : Info()}, "debug" : True}))
server.initialize_websockets_manager()


try:
	server.serve_forever()
except KeyboardInterrupt:
	server.server_close()

Tidbits

class Route(Route):  
    def run(self, data, handler):  
        # Called when a message for that particular route is received.  
    
    def start(self, handler):  
        # Called once the connection is ready.


class Handler(undergr0und.Server | undergr0und.Client):
    def ready(self):
        # Called once the handler has been initialised.

Protocol

The first 4 bytes of every message are reserved for the data type and the route id. All following bytes contain the actual data. (struct: Bh)

Data type Route ID Data
1 2 *

Data Types

Data type ID
String 0
Dictionary 1
List 1
Bytes 2
Integer 3
Float 4
Indexed dictionary 5
None 6

Error Codes

Error Code
Invalid Route 1
Invalid Metadata Layout 2
Invalid Data Type 3