import datetime import json import urllib.request, urllib.error, urllib.parse from urllib.parse import urlencode from collections import namedtuple BASE_URL = "" class Tunnel: def __init__( self, config={}, name=None, uri=None, public_url=None, proto=None, metrics=None): self.config = config self.name = name self.uri = uri self.public_url = public_url self.proto = proto self.metrics = metrics def __repr__(self): return ("<Tunnel '%s'>" % self.config["addr"]) \ if self.config.get("addr", None) else "<pending Tunnel>" def get_requests(self, limit=99999): requests = [] params = {"tunnel_name": self.name, "limit": limit} for x in api("requests/http", params=params)["requests"]: request = Request(x["id"], x["tunnel_name"], x["remote_addr"], x["start"], x["duration"], x["request"], x["response"]) requests.append(request) return requests def _set(self, data={}): self.config = data["config"] if data else {} self.name = data["name"] if data else None self.uri = data["uri"] if data else None self.public_url = data["public_url"] if data else None self.proto = data["proto"] if data else None self.metrics = data["metrics"] if data else None def refresh(self): if not self.name: raise Exception("Tunnel not yet started") data = api("tunnels/%s" % self.name) self._set(data) def start(self): if self.name: raise Exception("Tunnel already started") data = api("tunnels", "POST", self.config) self._set(data) def stop(self): api("tunnels/%s" % self.name, "DELETE") self._set() class Request: def __init__(self, id, tunnel, remote_addr, start, duration, request, response): self.id = id self.remote_addr = remote_addr self.start = datetime.datetime.strptime(start, "%Y-%m-%dT%H:%M:%SZ") self.duration = duration self.request = namedtuple("Request", list(request.keys()))(**request) self.response = namedtuple("Response", list(response.keys()))(**response) try: data = api("tunnels/%s" % tunnel) self.tunnel = Tunnel(data["config"], data["name"], data["uri"], data["public_url"], data["proto"], data["metrics"]) except: self.tunnel = None def __repr__(self): return "<Request id %s>" % self.id def replay(self, tunnel=None): api("requests/http", "POST", {"id": self.id, "tunnel_name": self.tunnel.name if self.tunnel else tunnel}) def api(endpoint, method="GET", data=None, params=[]): base_url = BASE_URL or "http://127.0.0.1:4040/" if params: endpoint += "?%s" % urlencode([(x, params[x]) for x in params]) request = urllib.request.Request("%sapi/%s" % (base_url, endpoint)) if method != "GET": request.get_method = lambda: method request.add_header("Content-Type", "application/json") response = urllib.request.urlopen(request, json.dumps(data) if data else None) try: return json.loads(response.read()) except: return None def get_tunnels(): tunnels = [] for x in api("tunnels")["tunnels"]: tunnel = Tunnel(x["config"], x["name"], x["uri"], x["public_url"], x["proto"], x["metrics"]) tunnels.append(tunnel) return tunnels def get_tunnel(id): try: data = api("tunnels/%s" % str(id)) return Tunnel(data["config"], data["name"], data["uri"], data["public_url"], data["proto"], data["metrics"]) except: return None def get_requests(): requests = [] for x in api("requests/http")["requests"]: request = Request(x["id"], x["tunnel_name"], x["remote_addr"], x["start"], x["duration"], x["request"], x["response"]) requests.append(request) return requests def get_request(id): try: data = api("requests/http/%s" % str(id)) return Request(x["id"], x["tunnel_name"], x["remote_addr"], x["start"], x["duration"], x["request"], x["response"]) except: return None