diff options
Diffstat (limited to 'tools/gateway.py')
| -rw-r--r-- | tools/gateway.py | 66 |
1 files changed, 45 insertions, 21 deletions
diff --git a/tools/gateway.py b/tools/gateway.py index 81a70af..3129773 100644 --- a/tools/gateway.py +++ b/tools/gateway.py @@ -1,18 +1,22 @@ #!/usr/bin/env python3 -"""gateway.py - a host-side "link cable adapter" for gbos networking. +"""gateway.py - host-side "link cable adapter" for gbos networking. -Wraps the emulator, owns its link-port serial (stdio), and speaks SLIP. This -milestone just ECHOES every frame back (loopback), so `necho` on gbos can prove -a framed round-trip. Console (ASCII) bytes on the same channel are printed. +Wraps the emulator, owns its link-port serial (stdio), speaks SLIP, and does +the real network work the Game Boy can't (DNS/TCP/HTTP). Modes: -usage: tools/gateway.py [rom] + echo - bounce every frame back (loopback test) + http - treat each frame as a URL, HTTP GET it, stream the body back + +Reply frames are typed: b'D'+chunk for data, b'E' to end the stream. + +usage: tools/gateway.py [--mode echo|http] [--cmd "wget example.com"] [--rom PATH] """ -import os, sys, subprocess, threading, time +import os, sys, subprocess, threading, time, argparse, urllib.request EMU = os.path.expanduser("~/dev/gbc/build/sl0pboy") -ROM = sys.argv[1] if len(sys.argv) > 1 else os.path.join(os.path.dirname(__file__), "..", "gbos.gb") - END, ESC, ESC_END, ESC_ESC = 0xC0, 0xDB, 0xDC, 0xDD +CHUNK = 200 +LIMIT = 800 # cap the fetched body (link is ~1-32 KB/s) def slip_encode(payload): out = bytearray([END]) @@ -23,13 +27,28 @@ def slip_encode(payload): out.append(END) return bytes(out) -def handle_frame(payload): - """The 'network'. For now: echo. (Later: DNS/TCP/HTTP/IRC.)""" - sys.stderr.write("\n[gw] frame in : %r\n" % bytes(payload)) - sys.stderr.write("[gw] frame out: %r (echo)\n" % bytes(payload)) - return bytes(payload) +def http_fetch(payload): + url = bytes(payload).decode("latin1", "replace").strip() + if not url.startswith("http"): + url = "http://" + url + sys.stderr.write("\n[gw] GET %s\n" % url) + try: + req = urllib.request.Request(url, headers={"User-Agent": "gbos/0.1"}) + with urllib.request.urlopen(req, timeout=8) as r: + body = r.read(LIMIT) + except Exception as e: + body = ("ERR: " + str(e)).encode() + sys.stderr.write("[gw] %d bytes\n" % len(body)) + frames = [b"D" + body[i:i+CHUNK] for i in range(0, len(body), CHUNK)] + frames.append(b"E") + return frames + +def handle(payload, mode): + if mode == "http": + return http_fetch(payload) + return [bytes(payload)] # echo -def reader(out, inp): +def reader(out, inp, mode): buf, in_frame, esc = bytearray(), False, False while True: b = out.read(1) @@ -38,8 +57,8 @@ def reader(out, inp): c = b[0] if c == END: if in_frame and buf: - reply = handle_frame(buf) - inp.write(slip_encode(reply)); inp.flush() + for f in handle(buf, mode): + inp.write(slip_encode(f)); inp.flush() buf, in_frame, esc = bytearray(), False, False else: buf, in_frame, esc = bytearray(), True, False @@ -55,12 +74,17 @@ def reader(out, inp): sys.stderr.flush() def main(): - p = subprocess.Popen([EMU, "--headless", "--uncapped", ROM], + ap = argparse.ArgumentParser() + ap.add_argument("--mode", default="echo", choices=["echo", "http"]) + ap.add_argument("--cmd", default="necho") + ap.add_argument("--rom", default=os.path.join(os.path.dirname(__file__), "..", "gbos.gb")) + a = ap.parse_args() + p = subprocess.Popen([EMU, "--headless", "--uncapped", a.rom], stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=0) - threading.Thread(target=reader, args=(p.stdout, p.stdin), daemon=True).start() - time.sleep(1.0) # boot to the shell - p.stdin.write(b"necho\n"); p.stdin.flush() - time.sleep(2.0) # let the framed round-trip happen + threading.Thread(target=reader, args=(p.stdout, p.stdin, a.mode), daemon=True).start() + time.sleep(1.0) + p.stdin.write((a.cmd + "\n").encode()); p.stdin.flush() + time.sleep(4.0) p.stdin.write(b"exit\n"); p.stdin.flush() try: p.wait(timeout=3) except Exception: p.kill() |
