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;
Here is a updated Version that should deal with ipv6 too. Feedback (very) welcome :) Cheers, Michael
=== modified file 'apt-avahi-discover' --- apt-avahi-discover 2013-04-09 15:42:27 +0000 +++ apt-avahi-discover 2013-08-26 13:35:30 +0000 @@ -3,10 +3,39 @@ # 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) + if is_ipv6(addr): + self.create_socket(socket.AF_INET6, socket.SOCK_STREAM) + self.connect( (addr[0], addr[1], 0, 0) ) + else: + 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 +55,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 +66,28 @@ # 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)) + # 3s timeout, arbitray + asyncore.loop(timeout=3) + if "--debug" in sys.argv: + sys.stderr.write("%s\n" % sorted(hosts)) + fastest_host = sorted(hosts)[0] + fastest_address = fastest_host.address + return fastest_address if __name__ == "__main__":