diff --git a/src/util/UpnpPunch.py b/src/util/UpnpPunch.py index 3694ba2b3..9fbf8f2c7 100644 --- a/src/util/UpnpPunch.py +++ b/src/util/UpnpPunch.py @@ -7,6 +7,7 @@ from xml.parsers.expat import ExpatError from gevent import socket +import gevent # Relevant UPnP spec: # http://www.upnp.org/specs/gw/UPnP-gw-WANIPConnection-v1-Service.pdf @@ -46,7 +47,7 @@ def perform_m_search(local_ip): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - sock.bind((local_ip, 10000)) + sock.bind((local_ip, 0)) sock.sendto(ssdp_request, ('239.255.255.250', 1900)) if local_ip == "127.0.0.1": @@ -295,20 +296,40 @@ def _communicate_with_igd(port=15441, Manage sending a message generated by 'fn'. """ - # Retry every ip 'retries' times - local_ips = _get_local_ips() * retries + local_ips = _get_local_ips() success = False + def job(local_ip): + for retry in range(retries): + try: + _orchestrate_soap_request(local_ip, port, fn, desc, protos) + return True + except (UpnpError, IGDError) as e: + logging.debug('Upnp request using "{0}" failed: {1}'.format( + local_ip, e)) + gevent.sleep(1) + return False + + threads = [] + for local_ip in local_ips: - try: - _orchestrate_soap_request(local_ip, port, fn, desc, protos) + thread = gevent.spawn(job, local_ip) + threads.append(thread) + gevent.sleep(0.1) + if any([thread.value for thread in threads]): success = True break - except (UpnpError, IGDError) as e: - logging.debug('Upnp request using "{0}" failed: {1}'.format( - local_ip, e)) - success = False - continue + + # Wait another 10sec for competition or any positibe result + for _ in range(10): + all_done = all([thread.value != None for thread in threads]) + any_succeed = any([thread.value for thread in threads]) + if all_done or any_succeed: + break + gevent.sleep(1) + + if any([thread.value for thread in threads]): + success = True if not success: raise UpnpError( @@ -330,7 +351,7 @@ def ask_to_close_port(port=15441, desc="UpnpPunch", retries=3, protos=("TCP", "U # retries=1 because multiple successes cause 500 response and failure _communicate_with_igd(port=port, desc=desc, - retries=1, + retries=retries, fn=_create_close_message, protos=protos) @@ -338,15 +359,15 @@ def ask_to_close_port(port=15441, desc="UpnpPunch", retries=3, protos=("TCP", "U if __name__ == "__main__": from gevent import monkey - monkey.patch_socket() + monkey.patch_all() logging.getLogger().setLevel(logging.DEBUG) import time s = time.time() print "Opening port..." - print ask_to_open_port(15443, "ZeroNet", retries=3, protos=["TCP"]) + print ask_to_open_port(15443, "ZeroNet",protos=["TCP"]) print "Done in", time.time()-s print "Closing port..." - print ask_to_close_port(15443, "ZeroNet", retries=3, protos=["TCP"]) + print ask_to_close_port(15443, "ZeroNet", protos=["TCP"]) print "Done in", time.time()-s