commit a3c045f340661c9256d08de86dc70e055feae25a Author: David Fifield david@bamsoftware.com Date: Fri May 20 05:07:48 2011 -0700
Make the facilitator use real HTTP in its responses. --- README | 12 +++++++----- design.txt | 24 ++++++++++++------------ facilitator.py | 29 ++++++++++++++++++++++++++++- 3 files changed, 47 insertions(+), 18 deletions(-)
diff --git a/README b/README index 067f92e..4e58907 100644 --- a/README +++ b/README @@ -87,16 +87,18 @@ with the caveat that the server also has to serve a crossdomain policy. The Tor client needs to be able to listen for an incoming connection, which generally means not being behind NAT.
-Clients register with the facilitator by sending an HTTP-like message: +Clients register with the facilitator by sending an HTTP message: POST / HTTP/1.0\r\n \r\n client=:9000 - -The Flash proxy also gets a client address using something like HTTP: +The Flash proxy also gets a client address over HTTP: GET / HTTP/1.0\r\n \r\n -The server sends back an address specification (no HTTP header): - 192.168.0.102:8888 +The server sends back an address specification in an HTTP respose. + HTTP/1.0 200 OK\r\n + Server: BaseHTTP/0.3 Python/2.5.2\r\n + \r\n + client=1.2.3.4%3A9000
== ActionScript programming
diff --git a/design.txt b/design.txt index 4776b73..abc53a4 100644 --- a/design.txt +++ b/design.txt @@ -108,10 +108,11 @@ Design of Flash proxies
client=[<address>]:<port>
- (The facilitator sends back no data in response.) If the connector - omits the [<address>] part, the facilitator will automatically fill it - in based on the HTTP client address, which means the connector doesn't - have to know its external address. + The facilitator sends a 200 reply if the registration was successful + and an error status otherwise. If the connector omits the [<address>] + part, the facilitator will automatically fill it in based on the HTTP + client address, which means the connector doesn't have to know its + external address.
The connector solves the impedance mismatch between the Tor client and the Flash proxy, both of which want to make outgoing connections to @@ -136,22 +137,21 @@ Design of Flash proxies GET / HTTP/1.0
- The response is not really HTTP: the data follows immediately with no - HTTP header. An empty document means that there are no clients - registered. Otherwise, the response looks like this: + The response code is 200 and the body looks like this:
- <address>:<port> + client=[<address>:<port>]
- Both <address> and <port> are required. + If <address>:<port> is missing, it means that there are no client + registrations for this proxy.
The Flash proxy may serve more than one relay–client pair at once. It limits its own bandwidth to 10 KB/s (combined upload/download).
8. Behavior of the facilitator
- The faciliator is a pseudo-HTTP server that handles client POST - registrations and proxy GET requests according to the formats given - above. The facilitator listens on port 9002. + The faciliator is a HTTP server that handles client POST registrations + and proxy GET requests according to the formats given above. The + facilitator listens on port 9002.
In the current implementation, the facilitator forgets a client registration after giving it to a Flash proxy. The client must diff --git a/facilitator.py b/facilitator.py index d5bac75..56a9774 100755 --- a/facilitator.py +++ b/facilitator.py @@ -10,6 +10,7 @@ import socket import sys import threading import time +import urllib
DEFAULT_ADDRESS = "0.0.0.0" DEFAULT_PORT = 9002 @@ -193,20 +194,23 @@ class Handler(BaseHTTPServer.BaseHTTPRequestHandler): reg = REGS.fetch() if reg: log(u"proxy %s gets %s (now %d)" % (format_addr(self.client_address), unicode(reg), len(REGS))) - self.request.send(str(reg)) + self.send_client(reg) else: log(u"proxy %s gets none" % format_addr(self.client_address)) + self.send_client(None)
def do_POST(self): data = self.rfile.readline(1024).strip() try: vals = cgi.parse_qs(data, False, True) except ValueError, e: + self.send_error(400) log(u"client %s POST syntax error: %s" % (format_addr(self.client_address), repr(str(e)))) return
client_specs = vals.get("client") if client_specs is None or len(client_specs) != 1: + self.send_error(400) log(u"client %s missing "client" param" % format_addr(self.client_address)) return val = client_specs[0] @@ -214,6 +218,7 @@ class Handler(BaseHTTPServer.BaseHTTPRequestHandler): try: reg = Reg.parse(val, self.client_address[0]) except ValueError, e: + self.send_error(400) log(u"client %s syntax error in %s: %s" % (format_addr(self.client_address), repr(val), repr(str(e)))) return
@@ -223,10 +228,32 @@ class Handler(BaseHTTPServer.BaseHTTPRequestHandler): else: log(u"client %s %s (already present, now %d)" % (format_addr(self.client_address), unicode(reg), len(REGS)))
+ self.send_response(200) + self.end_headers() + + def send_error(self, code, message = None): + self.send_response(code) + self.end_headers() + if message: + self.wfile.write(message) + def log_message(self, format, *args): msg = format % args log(u"message from HTTP handler for %s: %s" % (format_addr(self.client_address), repr(msg)))
+ def send_client(self, reg): + if reg: + client_str = str(reg) + else: + # Send an empty string rather than a 404 or similar because Flash + # Player's URLLoader can't always distinguish a 404 from, say, + # "server not found." + client_str = "" + self.send_response(200) + self.send_header("Content-Type", "x-www-form-urlencoded") + self.end_headers() + self.request.send(urllib.urlencode({"client": client_str})) + REGS = RegSet()
opts, args = getopt.gnu_getopt(sys.argv[1:], "dhl:", ["debug", "help", "log="])