def _redirect(self, new_url, old_query): """Update announce URL from HTTP redirect""" if self._redirects >= self.MAX_REDIRECTS: raise IOError(('http error', 500, 'Internal Server Error: Redirect Recursion')) scheme, netloc, path, query, frag = urllib.parse.urlsplit(new_url) # If we got our own query string back, remove it oq_dict = urllib.parse.parse_qs(old_query) new_query = urllib.parse.urlencode( (key, val) for key, val in urllib.parse.parse_qsl(query) if val != oq_dict.get(key, '')) tracker_url = urllib.parse.urlunsplit( (scheme, netloc, path, new_query, frag)) with self.announcer_lock: # It's possible to have the same redirect happen if tracker_url in self.announcers and \ self.announcers[tracker_url] == self: return self._redirects += 1 self.announcers[tracker_url] = self self.stream = SharedStream(tracker_url) self.basequery = path + '?' + new_query + '&'[:bool(new_query)]
def __new__(cls, tracker_url, port, *args, **kwargs): """Return the Announcer associated with the tracker URL, creating a new one, if needed. Client port is made available to Announcer objects, but is assumed not to vary within a program, so calling with the same URL and two different ports will return the same object.""" # Use Announcer() to allow scheme to decide on subclass if cls is Announcer: scheme = urllib.parse.urlsplit(tracker_url)[0] return cls.subclasses[scheme](tracker_url, port, *args, **kwargs) with cls.announcer_lock: # Assume port will not change within single Python instance # If this changes, change key to (tracker_url, port) if tracker_url not in cls.announcers: announcer = super(Announcer, cls).__new__(cls) # Keys allow trackers to recognize clients with IP changes announcer.key = random.randint(0, 0xffffffff).to_bytes(4, 'big') announcer.stream = SharedStream(tracker_url) cls.announcers[tracker_url] = announcer return cls.announcers[tracker_url]