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__":