provide simple recovery/escape from apparent redirect recursion.  If the
number of entries into http_error_302 exceeds the value set for the maxtries
attribute (which defaults to 10), the recursion is exited by calling
the http_error_500 method (or if that is not defined, http_error_default).
diff --git a/Lib/urllib.py b/Lib/urllib.py
index c5d85fc..62b0787 100644
--- a/Lib/urllib.py
+++ b/Lib/urllib.py
@@ -513,6 +513,8 @@
     def __init__(self, *args):
         apply(URLopener.__init__, (self,) + args)
         self.auth_cache = {}
+        self.tries = 0
+        self.maxtries = 10
 
     def http_error_default(self, url, fp, errcode, errmsg, headers):
         """Default error handling -- don't raise an exception."""
@@ -520,7 +522,21 @@
 
     def http_error_302(self, url, fp, errcode, errmsg, headers, data=None):
         """Error 302 -- relocated (temporarily)."""
-        # XXX The server can force infinite recursion here!
+        self.tries += 1
+        if self.maxtries and self.tries >= self.maxtries:
+            if hasattr(self, "http_error_500"):
+                meth = self.http_error_500
+            else:
+                meth = self.http_error_default
+            self.tries = 0
+            return meth(url, fp, 500,
+                        "Internal Server Error: Redirect Recursion", headers)
+        result = self.redirect_internal(url, fp, errcode, errmsg, headers,
+                                        data)
+        self.tries = 0
+        return result
+
+    def redirect_internal(self, url, fp, errcode, errmsg, headers, data):
         if headers.has_key('location'):
             newurl = headers['location']
         elif headers.has_key('uri'):