[flashproxy/master] Move some common code to fac.py.

commit af7de115115516435388503fe21c22a03d3b7445 Author: David Fifield <david@bamsoftware.com> Date: Sat Aug 11 08:53:49 2012 -0700 Move some common code to fac.py. --- fac.py | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++ facilitator | 154 +++++------------------------------------------------------ 2 files changed, 145 insertions(+), 142 deletions(-) diff --git a/fac.py b/fac.py new file mode 100644 index 0000000..ab8157c --- /dev/null +++ b/fac.py @@ -0,0 +1,133 @@ +import re +import socket + +def parse_addr_spec(spec, defhost = None, defport = None): + host = None + port = None + m = None + # IPv6 syntax. + if not m: + m = re.match(ur'^\[(.+)\]:(\d+)$', spec) + if m: + host, port = m.groups() + af = socket.AF_INET6 + if not m: + m = re.match(ur'^\[(.+)\]:?$', spec) + if m: + host, = m.groups() + af = socket.AF_INET6 + # IPv4 syntax. + if not m: + m = re.match(ur'^(.+):(\d+)$', spec) + if m: + host, port = m.groups() + af = socket.AF_INET + if not m: + m = re.match(ur'^:?(\d+)$', spec) + if m: + port, = m.groups() + af = 0 + if not m: + host = spec + af = 0 + host = host or defhost + port = port or defport + if not (host and port): + raise ValueError("Bad address specification \"%s\"" % spec) + return af, host, int(port) + +def format_addr(addr): + host, port = addr + if not host: + return u":%d" % port + # Numeric IPv6 address? + try: + addrs = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM, socket.IPPROTO_TCP, socket.AI_NUMERICHOST) + af = addrs[0][0] + except socket.gaierror, e: + af = 0 + if af == socket.AF_INET6: + return u"[%s]:%d" % (host, port) + else: + return u"%s:%d" % (host, port) + +def skip_space(pos, line): + """Skip a (possibly empty) sequence of space characters (the ASCII character + '\x20' exactly). Returns a pair (pos, num_skipped).""" + begin = pos + while pos < len(line) and line[pos] == "\x20": + pos += 1 + return pos, pos - begin + +TOKEN_CHARS = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-") +def get_token(pos, line): + begin = pos + while pos < len(line) and line[pos] in TOKEN_CHARS: + pos += 1 + if begin == pos: + raise ValueError("No token found at position %d" % pos) + return pos, line[begin:pos] + +def get_quoted_string(pos, line): + chars = [] + if not (pos < len(line) and line[pos] == '"'): + raise ValueError("Expected '\"' at beginning of quoted string.") + pos += 1 + while pos < len(line) and line[pos] != '"': + if line[pos] == '\\': + pos += 1 + if not (pos < len(line)): + raise ValueError("End of line after backslash in quoted string") + chars.append(line[pos]) + pos += 1 + if not (pos < len(line) and line[pos] == '"'): + raise ValueError("Expected '\"' at end of quoted string.") + pos += 1 + return pos, "".join(chars) + +def parse_transaction(line): + """A transaction is a command followed by zero or more key-value pairs. Like so: + COMMAND KEY="VALUE" KEY="\"ESCAPED\" VALUE" + Values must be quoted. Any byte value may be escaped with a backslash. + Returns a pair: (COMMAND, ((KEY1, VALUE1), (KEY2, VALUE2), ...)). + """ + pos = 0 + pos, skipped = skip_space(pos, line) + pos, command = get_token(pos, line) + + pairs = [] + while True: + pos, skipped = skip_space(pos, line) + if not (pos < len(line)): + break + if skipped == 0: + raise ValueError("Expected space before key-value pair") + pos, key = get_token(pos, line) + if not (pos < len(line) and line[pos] == '='): + raise ValueError("No '=' found after key") + pos += 1 + pos, value = get_quoted_string(pos, line) + pairs.append((key, value)) + return command, tuple(pairs) + +def param_first(key, params): + for k, v in params: + if key == k: + return v + return None + +def quote_string(s): + chars = [] + for c in s: + if c == "\\": + c = "\\\\" + elif c == "\"": + c = "\\\"" + chars.append(c) + return "\"" + "".join(chars) + "\"" + +def render_transaction(command, *params): + parts = [command] + for key, value in params: + parts.append("%s=%s" % (key, quote_string(value))) + return " ".join(parts) diff --git a/facilitator b/facilitator index 2ab6491..d1e25a4 100755 --- a/facilitator +++ b/facilitator @@ -3,12 +3,13 @@ import SocketServer import getopt import os -import re import socket import sys import threading import time +import fac + LISTEN_ADDRESS = "127.0.0.1" DEFAULT_LISTEN_PORT = 9002 DEFAULT_RELAY_PORT = 9001 @@ -32,10 +33,10 @@ class options(object): @staticmethod def set_relay_spec(spec): - af, host, port = parse_addr_spec(spec, defport = DEFAULT_RELAY_PORT) + af, host, port = fac.parse_addr_spec(spec, defport = DEFAULT_RELAY_PORT) # Resolve to get an IP address. addrs = socket.getaddrinfo(host, port, af) - options.relay_spec = format_addr(addrs[0][4]) + options.relay_spec = fac.format_addr(addrs[0][4]) def usage(f = sys.stdout): print >> f, """\ @@ -71,144 +72,13 @@ def log(msg): finally: log_lock.release() -def parse_addr_spec(spec, defhost = None, defport = None): - host = None - port = None - m = None - # IPv6 syntax. - if not m: - m = re.match(ur'^\[(.+)\]:(\d+)$', spec) - if m: - host, port = m.groups() - af = socket.AF_INET6 - if not m: - m = re.match(ur'^\[(.+)\]:?$', spec) - if m: - host, = m.groups() - af = socket.AF_INET6 - # IPv4 syntax. - if not m: - m = re.match(ur'^(.+):(\d+)$', spec) - if m: - host, port = m.groups() - af = socket.AF_INET - if not m: - m = re.match(ur'^:?(\d+)$', spec) - if m: - port, = m.groups() - af = 0 - if not m: - host = spec - af = 0 - host = host or defhost - port = port or defport - if not (host and port): - raise ValueError("Bad address specification \"%s\"" % spec) - return af, host, int(port) - -def format_addr(addr): - host, port = addr - if not host: - return u":%d" % port - # Numeric IPv6 address? - try: - addrs = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM, socket.IPPROTO_TCP, socket.AI_NUMERICHOST) - af = addrs[0][0] - except socket.gaierror, e: - af = 0 - if af == socket.AF_INET6: - return u"[%s]:%d" % (host, port) - else: - return u"%s:%d" % (host, port) - -def skip_space(pos, line): - """Skip a (possibly empty) sequence of space characters (the ASCII character - '\x20' exactly). Returns a pair (pos, num_skipped).""" - begin = pos - while pos < len(line) and line[pos] == "\x20": - pos += 1 - return pos, pos - begin - -TOKEN_CHARS = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-") -def get_token(pos, line): - begin = pos - while pos < len(line) and line[pos] in TOKEN_CHARS: - pos += 1 - if begin == pos: - raise ValueError("No token found at position %d" % pos) - return pos, line[begin:pos] - -def get_quoted_string(pos, line): - chars = [] - if not (pos < len(line) and line[pos] == '"'): - raise ValueError("Expected '\"' at beginning of quoted string.") - pos += 1 - while pos < len(line) and line[pos] != '"': - if line[pos] == '\\': - pos += 1 - if not (pos < len(line)): - raise ValueError("End of line after backslash in quoted string") - chars.append(line[pos]) - pos += 1 - if not (pos < len(line) and line[pos] == '"'): - raise ValueError("Expected '\"' at end of quoted string.") - pos += 1 - return pos, "".join(chars) - -def parse_command(line): - """A line is a command followed by zero or more key-value pairs. Like so: - COMMAND KEY="VALUE" KEY="\"ESCAPED\" VALUE" - Values must be quoted. Any byte value may be escaped with a backslash. - Returns a pair: (COMMAND, ((KEY1, VALUE1), (KEY2, VALUE2), ...)). - """ - pos = 0 - pos, skipped = skip_space(pos, line) - pos, command = get_token(pos, line) - - pairs = [] - while True: - pos, skipped = skip_space(pos, line) - if not (pos < len(line)): - break - if skipped == 0: - raise ValueError("Expected space before key-value pair") - pos, key = get_token(pos, line) - if not (pos < len(line) and line[pos] == '='): - raise ValueError("No '=' found after key") - pos += 1 - pos, value = get_quoted_string(pos, line) - pairs.append((key, value)) - return command, tuple(pairs) - -def param_first(key, params): - for k, v in params: - if key == k: - return v - return None - -def quote_string(s): - chars = [] - for c in s: - if c == "\\": - c = "\\\\" - elif c == "\"": - c = "\\\"" - chars.append(c) - return "\"" + "".join(chars) + "\"" - -def render_params(params): - parts = [] - for key, value in params: - parts.append("%s=%s" % (key, quote_string(value))) - return " ".join(parts) - class TCPReg(object): def __init__(self, host, port): self.host = host self.port = port def __unicode__(self): - return format_addr((self.host, self.port)) + return fac.format_addr((self.host, self.port)) def __str__(self): return unicode(self).encode("UTF-8") @@ -223,7 +93,7 @@ class Reg(object): @staticmethod def parse(spec, defhost = None, defport = None): try: - af, host, port = parse_addr_spec(spec, defhost, defport) + af, host, port = fac.parse_addr_spec(spec, defhost, defport) except ValueError: pass else: @@ -330,9 +200,9 @@ class Handler(SocketServer.StreamRequestHandler): if not (len(line) > 0 and line[-1] == '\n'): raise ValueError("No newline at end of string returned by readline") try: - command, params = parse_command(line[:-1]) + command, params = fac.parse_transaction(line[:-1]) except ValueError, e: - log("parse_command: %s" % e) + log("fac.parse_transaction: %s" % e) self.send_error() return False @@ -355,14 +225,14 @@ class Handler(SocketServer.StreamRequestHandler): if reg: log(u"proxy gets %s, relay %s (now %d)" % (safe_str(unicode(reg)), options.relay_spec, len(REGS))) - print >> self.wfile, "OK", render_params((("CLIENT", str(reg)), ("RELAY", options.relay_spec))) + print >> self.wfile, fac.render_transaction("OK", ("CLIENT", str(reg)), ("RELAY", options.relay_spec)) else: log(u"proxy gets none") - print >> self.wfile, "NONE" + print >> self.wfile, fac.render_transaction("NONE") return True def do_PUT(self, params): - client_spec = param_first("CLIENT", params) + client_spec = fac.param_first("CLIENT", params) if client_spec is None: log(u"PUT missing CLIENT param") self.send_error() @@ -433,7 +303,7 @@ The -r option is required. Give it the relay that will be sent to proxies. server = Server(addrinfo[4], Handler) - log(u"start on %s" % format_addr(addrinfo[4])) + log(u"start on %s" % fac.format_addr(addrinfo[4])) log(u"using relay address %s" % options.relay_spec) if options.daemonize:
participants (1)
-
dcf@torproject.org