On Fri, Aug 09, 2013 at 04:26:58PM +0200, Paul Wise wrote:
> On Thu, 2013-08-08 at 16:23 +0200, Michael Vogt wrote:
> 
> > I like this suggestion a lot, do you think its sufficient to special
> > case localhost and (always) prefer that or something more
> > sophisticated like measuring how long a connect to the port takes?
> 
> localhost won't match what is returned from avahi but anyway I would
> suggest prefer the local machine. If there is no proxy on the local
> machine, then use whichever proxy connects first.
> 
> pabs@chianamo ~ $ avahi-browse -kprt _apt_proxy._tcp
> +;wlan0;IPv4;Squid\032deb\032proxy\032on\032someothersystem;_apt_proxy._tcp;local
> +;wlan0;IPv4;apt-cacher-ng\032proxy\032on\032chianamo;_apt_proxy._tcp;local
> =;wlan0;IPv4;Squid\032deb\032proxy\032on\032rocinante;_apt_proxy._tcp;local;someothersystem.local;10.0.0.134;8000;
> =;wlan0;IPv4;apt-cacher-ng\032proxy\032on\032chianamo;_apt_proxy._tcp;local;chianamo.local;10.0.0.134;3142;

Thanks and sorry for my slow reply, could you please check if the
attached patch helps with your use-case?

Thanks,
 Michael
=== modified file 'apt-avahi-discover'
--- apt-avahi-discover	2013-04-09 15:42:27 +0000
+++ apt-avahi-discover	2013-08-26 09:21:53 +0000
@@ -3,10 +3,35 @@
 # use avahi to find a _apt_proxy._tcp provider and return
 # a http proxy string suitable for apt
 
+import asyncore
+import functools
 import socket
+import sys
+import time
 from subprocess	import Popen, PIPE
 
 
+@functools.total_ordering
+class AptAvahiClient(asyncore.dispatcher):
+    def __init__(self, addr):
+        asyncore.dispatcher.__init__(self)
+        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
+        self.connect(addr)
+        self._time_init = time.time()
+        self.time_to_connect = sys.maxint
+        self.address = addr
+    def handle_connect(self):
+        self.time_to_connect = time.time() - self._time_init
+        self.close()
+    def __eq__(self, other):
+        return self.time_to_connect == other.time_to_connect
+    def __lt__(self, other):
+        return self.time_to_connect < other.time_to_connect
+    def __repr__(self):
+        return "<%s> %s: %s" % (
+            self.__class__.__name__, self.addr, self.time_to_connect)
+
+
 def is_ipv6(a):
     return ':' in a
 
@@ -26,7 +51,7 @@
         if line.startswith('='):
             tokens = line.split(';')
             addr = tokens[7]
-            port = tokens[8]
+            port = int(tokens[8])
             if is_ipv6(addr):
                 # We need to skip ipv6 link-local addresses since 
                 # APT can't use them
@@ -37,15 +62,25 @@
 
     # Run through the offered addresses and see if we we have a bound local
     # address for it.
+    addrs = []
     for (ip, port) in addr6 + addr4:
         try:
             res = socket.getaddrinfo(ip, port, 0, 0, 0, socket.AI_ADDRCONFIG)
             if res:
-                return (ip, port)
+                addrs.append((ip, port))
         except socket.gaierror:
             pass
-    # nothing found
-    return None
+    if not addrs:
+        return None
+    
+    # sort by answering speed
+    hosts = []
+    for addr in addrs:
+        hosts.append(AptAvahiClient(addr))
+    asyncore.loop()
+    fastest_host = sorted(hosts)[0]
+    fastest_address = fastest_host.address
+    return fastest_address
 
 
 if __name__ == "__main__":

Reply via email to