commit 55841fece74c90019457092bf67ed4309b0d7312
Author: David Fifield <david(a)bamsoftware.com>
Date: Sun Sep 4 20:17:15 2011 -0700
Revert facilitator blocking until a reg is available.
This reverts
91cceeef62061d66732be7039d559f5fff49e8ef Document the facilitator's waiting until a reg is available.
c3ea89cb2a2c1e5f8496ec526b9d1c7451e2d06d Log the socket error when we fail to send a reg.
90510d7bf565f0cc9876665aa6e85b55c8e5a3c1 Make facilitator requests block until a reg is available.
This would be a nice model, but the URLLoader in ActionScript doesn't
work exactly the way I expect it to (at least with Flash Player in
Firefox), and it means that this "long polling" technique doesn't work
robustly.
The first time swfcat is loaded, everything is fine: it makes the
/crossdomain.xml request, then starts a URLLoader for /. If there is no
client to be served immediately, then the URLLoader just hangs until
there is one (this part works well).
What goes wrong is when you refresh the page (or navigate away and then
back again). I expected that the URLLoader would break its connection
when this happens, which would be noticed by the facilitator. Instead,
what seems to happen is the URLLoader's HTTP connection goes into limbo,
still alive and waiting for a response, but not hooked up to anything on
the client side. (I suppose this is an artifact of Flash Player using
the browser to make the request, so it might differ on other browsers.)
The new instance of swfcat asks for /crossdomain.xml, and claims to be
opening a URLLoader for /, but instead what appears to happen is the new
request gets queued behind the pending one that is in limbo; tcpdump
doesn't see any second request until the first is completed. Now, if a
client registers, its reg goes out over the in-limbo connection and
effectively disappears. The queued URLLoader request from the proxy gets
sent, but at this point there is no more client reg because it was
presumed to be handled by the first proxy request.
This queueing behavior is evident if you refresh the page, say, five
times, and then kill the facilitator: you see one HTTP request, then a
RST from the server, then four other immediate SYN-RST pairs.
So instead, we go back to the facilitator always returning immediately
(possibly without a client), and the proxy polling on a regular basis
for clients.
---
design.txt | 4 ++--
facilitator.py | 38 ++++++++++++++------------------------
2 files changed, 16 insertions(+), 26 deletions(-)
diff --git a/design.txt b/design.txt
index f33468c..55427b6 100644
--- a/design.txt
+++ b/design.txt
@@ -151,8 +151,8 @@ Design of flash proxies
client=[<rtmfp-id>]&relay=<address>:<port>
- The facilitator keeps the HTTP session alive and does not send a
- registration until one is available.
+ If the value for the client parameter is empty, it means that there are no
+ client registrations for this proxy.
The flash proxy may serve more than one relay–client pair at once.
diff --git a/facilitator.py b/facilitator.py
index bea3957..495cd3b 100755
--- a/facilitator.py
+++ b/facilitator.py
@@ -183,23 +183,11 @@ class RegSet(object):
finally:
self.cv.release()
- def add_front(self, reg):
- self.cv.acquire()
- try:
- if reg not in list(self.set):
- self.set.append(reg)
- self.cv.notify()
- return True
- else:
- return False
- finally:
- self.cv.release()
-
def fetch(self):
self.cv.acquire()
try:
- while not self.set:
- self.cv.wait()
+ if not self.set:
+ return None
return self.set.pop(0)
finally:
self.cv.release()
@@ -224,16 +212,13 @@ class Handler(BaseHTTPServer.BaseHTTPRequestHandler):
return
reg = REGS.fetch()
-
- try:
- self.send_client(reg)
+ if reg:
log(u"proxy %s gets %s, relay %s (now %d)" %
(proxy_addr_s, unicode(reg), options.relay_spec, len(REGS)))
- except socket.error, e:
- # Something went wrong; likely the proxy disconnected without
- # receiving a reg. Restore the reg to the front of the queue.
- REGS.add_front(reg)
- log(u"proxy %s gets none (%s)" % (proxy_addr_s, str(e)))
+ self.send_client(reg)
+ else:
+ log(u"proxy %s gets none" % proxy_addr_s)
+ self.send_client(None)
def do_POST(self):
client_addr_s = format_addr(self.client_address)
@@ -301,8 +286,13 @@ class Handler(BaseHTTPServer.BaseHTTPRequestHandler):
% (format_addr(self.client_address), repr(msg)))
def send_client(self, reg):
- client_str = str(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.send_header("Cache-Control", "no-cache")