diff options
author | Ben Sima <ben@bsima.me> | 2020-04-15 09:54:10 -0700 |
---|---|---|
committer | Ben Sima <ben@bsima.me> | 2020-04-15 10:06:56 -0700 |
commit | f4b8c0df041b063c0b47d2ec6c818a9c202fd833 (patch) | |
tree | 01ad246a83fda29c079847b3397ca6509a7f6106 /Que/client.py | |
parent | 6ed475ca94209ce92e75f48764cb9d361029ea26 (diff) |
Re-namespacing
Moving away from the DNS-driven namespacing toward more condensed names,
mostly because I don't like typing so much.
Diffstat (limited to 'Que/client.py')
-rwxr-xr-x | Que/client.py | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/Que/client.py b/Que/client.py new file mode 100755 index 0000000..3d9291d --- /dev/null +++ b/Que/client.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 +""" +simple client for que.run +""" + +import argparse +import configparser +import http.client +import os +import subprocess +import sys +import time +import urllib.parse +import urllib.request as request + +MAX_TIMEOUT = 99999999 # basically never timeout + + +def auth(args): + ns = args.target.split("/")[0] + if ns == "pub": + return None + else: + conf_file = os.path.expanduser("~/.config/que.conf") + if not os.path.exists(conf_file): + sys.exit("you need a ~/.config/que.conf") + cfg = configparser.ConfigParser() + cfg.read(conf_file) + return cfg[ns]["key"] + + +def send(args): + "Send a message to the que." + key = auth(args) + data = args.infile + req = request.Request(f"{args.host}/{args.target}") + req.add_header("User-AgenT", "Que/Client") + if key: + req.add_header("Authorization", key) + if args.serve: + while not time.sleep(1): + request.urlopen(req, data=data, timeout=MAX_TIMEOUT) + + else: + request.urlopen(req, data=data, timeout=MAX_TIMEOUT) + + +def recv(args): + "Receive a message from the que." + + def _recv(_req): + msg = autodecode(_req.read()) + print(msg) + if args.then: + subprocess.run( + args.then.replace("\msg", msg).replace("\que", args.target), shell=True + ) + + params = urllib.parse.urlencode({"poll": args.poll}) + req = request.Request(f"{args.host}/{args.target}?{params}") + req.add_header("User-Agent", "Que/Client") + key = auth(args) + if key: + req.add_header("Authorization", key) + with request.urlopen(req) as _req: + if args.poll: + while not time.sleep(1): + _recv(_req) + else: + _recv(_req) + + +def autodecode(b): + """Attempt to decode bytes `b` into common codecs, preferably utf-8. If + no decoding is available, just return the raw bytes. + + For all available codecs, see: + <https://docs.python.org/3/library/codecs.html#standard-encodings> + + """ + codecs = ["utf-8", "ascii"] + for codec in codecs: + try: + return b.decode(codec) + except UnicodeDecodeError: + pass + return b + + +def get_args(): + cli = argparse.ArgumentParser(description=__doc__) + cli.add_argument( + "--host", default="http://que.run", help="where que-server is running" + ) + cli.add_argument( + "--poll", default=False, action="store_true", help="stream data from the que" + ) + cli.add_argument( + "--then", + help=" ".join( + [ + "when polling, run this shell command after each response,", + "presumably for side effects," + "replacing '\que' with the target and '\msg' with the body of the response", + ] + ), + ) + cli.add_argument( + "--serve", + default=False, + action="store_true", + help=" ".join( + [ + "when posting to the que, do so continuously in a loop.", + "this can be used for serving a webpage or other file continuously", + ] + ), + ) + cli.add_argument( + "target", help="namespace and path of the que, like 'ns/path/subpath'" + ) + cli.add_argument( + "infile", + nargs="?", + type=argparse.FileType("rb"), + help="data to put on the que. Use '-' for stdin, otherwise should be a readable file", + ) + return cli.parse_args() + + +if __name__ == "__main__": + args = get_args() + try: + if args.infile: + send(args) + else: + recv(args) + except KeyboardInterrupt: + sys.exit(0) + except urllib.error.HTTPError as e: + print(e) + sys.exit(1) + except http.client.RemoteDisconnected as e: + print("disconnected... retrying in 5 seconds") + time.sleep(5) + if args.infile: + send(args) + else: + recv(args) |