| 660 | # have already seen. Do this by adding a handler-specific |
| 661 | # attribute to the Request object. |
| 662 | def http_error_302(self, req, fp, code, msg, headers): |
| 663 | # Some servers (incorrectly) return multiple Location headers |
| 664 | # (so probably same goes for URI). Use first header. |
| 665 | if "location" in headers: |
| 666 | newurl = headers["location"] |
| 667 | elif "uri" in headers: |
| 668 | newurl = headers["uri"] |
| 669 | else: |
| 670 | return |
| 671 | |
| 672 | # fix a possible malformed URL |
| 673 | urlparts = urlparse(newurl) |
| 674 | |
| 675 | # For security reasons we don't allow redirection to anything other |
| 676 | # than http, https or ftp. |
| 677 | |
| 678 | if urlparts.scheme not in ('http', 'https', 'ftp', ''): |
| 679 | raise HTTPError( |
| 680 | newurl, code, |
| 681 | "%s - Redirection to url '%s' is not allowed" % (msg, newurl), |
| 682 | headers, fp) |
| 683 | |
| 684 | if not urlparts.path and urlparts.netloc: |
| 685 | urlparts = list(urlparts) |
| 686 | urlparts[2] = "/" |
| 687 | newurl = urlunparse(urlparts) |
| 688 | |
| 689 | # http.client.parse_headers() decodes as ISO-8859-1. Recover the |
| 690 | # original bytes and percent-encode non-ASCII bytes, and any special |
| 691 | # characters such as the space. |
| 692 | newurl = quote( |
| 693 | newurl, encoding="iso-8859-1", safe=string.punctuation) |
| 694 | newurl = urljoin(req.full_url, newurl) |
| 695 | |
| 696 | # XXX Probably want to forget about the state of the current |
| 697 | # request, although that might interact poorly with other |
| 698 | # handlers that also use handler-specific request attributes |
| 699 | new = self.redirect_request(req, fp, code, msg, headers, newurl) |
| 700 | if new is None: |
| 701 | return |
| 702 | |
| 703 | # loop detection |
| 704 | # .redirect_dict has a key url if url was previously visited. |
| 705 | if hasattr(req, 'redirect_dict'): |
| 706 | visited = new.redirect_dict = req.redirect_dict |
| 707 | if (visited.get(newurl, 0) >= self.max_repeats or |
| 708 | len(visited) >= self.max_redirections): |
| 709 | raise HTTPError(req.full_url, code, |
| 710 | self.inf_msg + msg, headers, fp) |
| 711 | else: |
| 712 | visited = new.redirect_dict = req.redirect_dict = {} |
| 713 | visited[newurl] = visited.get(newurl, 0) + 1 |
| 714 | |
| 715 | # Don't close the fp until we are sure that we won't use it |
| 716 | # with HTTPError. |
| 717 | fp.read() |
| 718 | fp.close() |
| 719 | |