#!/usr/bin/env python3 # Licensed under GPLv3 # # Simple http server to allow user control of n2n edge nodes # # Currently only for demonstration # - needs nicer looking html written # - needs more json interfaces in edge # # Try it out with # http://localhost:8080/ # http://localhost:8080/edge/peer # http://localhost:8080/edge/super import argparse import socket import json import socketserver import http.server import signal import functools from http import HTTPStatus class JsonUDP(): """encapsulate communication with the edge""" def __init__(self, port): self.address = "127.0.0.1" self.port = port self.tag = 0 self.key = None self.debug = False self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) def _next_tag(self): tagstr = str(self.tag) self.tag = (self.tag + 1) % 1000 return tagstr def _cmdstr(self, msgtype, cmdline): """Create the full command string to send""" tagstr = self._next_tag() options = [tagstr] if self.key is not None: options += ['1'] # Flags set for auth key field options += [self.key] optionsstr = ':'.join(options) return tagstr, ' '.join((msgtype, optionsstr, cmdline)) def _rx(self, tagstr): """Wait for rx packets""" # TODO: there are no timeouts with any of the recv calls data, _ = self.sock.recvfrom(1024) data = json.loads(data.decode('utf8')) # TODO: We assume the first packet we get will be tagged for us # and be either an "error" or a "begin" assert(data['_tag'] == tagstr) if data['_type'] == 'error': raise ValueError('Error: {}'.format(data['error'])) assert(data['_type'] == 'begin') # Ideally, we would confirm that this is our "begin", but that # would need the cmd passed into this method, and that would # probably require parsing the cmdline passed to us :-( # assert(data['cmd'] == cmd) result = list() while True: data, _ = self.sock.recvfrom(1024) data = json.loads(data.decode('utf8')) if data['_tag'] != tagstr: # this packet is not for us, ignore it continue if data['_type'] == 'error': raise ValueError('Error: {}'.format(data['error'])) if data['_type'] == 'end': return result if data['_type'] != 'row': raise ValueError('Unknown data type {} from ' 'edge'.format(data['_type'])) # remove our boring metadata del data['_tag'] del data['_type'] if self.debug: print(data) result.append(data) def _call(self, msgtype, cmdline): """Perform a rpc call""" tagstr, msgstr = self._cmdstr(msgtype, cmdline) self.sock.sendto(msgstr.encode('utf8'), (self.address, self.port)) return self._rx(tagstr) def read(self, cmdline): return self._call('r', cmdline) def write(self, cmdline): return self._call('w', cmdline) indexhtml = """