diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/api/urlfetch.py --- a/thirdparty/google_appengine/google/appengine/api/urlfetch.py Tue Sep 16 01:18:49 2008 +0000 +++ b/thirdparty/google_appengine/google/appengine/api/urlfetch.py Tue Sep 16 02:28:33 2008 +0000 @@ -25,13 +25,17 @@ +import os import UserDict +import urllib2 +import urlparse from google.appengine.api import apiproxy_stub_map from google.appengine.api import urlfetch_service_pb from google.appengine.api.urlfetch_errors import * from google.appengine.runtime import apiproxy_errors +MAX_REDIRECTS = 5 GET = 1 POST = 2 @@ -153,7 +157,33 @@ return dict(self) -def fetch(url, payload=None, method=GET, headers={}, allow_truncated=False): +def _is_fetching_self(url, method): + """Checks if the fetch is for the same URL from which it originated. + + Args: + url: str, The URL being fetched. + method: value from _VALID_METHODS. + + Returns: + boolean indicating whether or not it seems that the app is trying to fetch + itself. + """ + if (method != GET or + "HTTP_HOST" not in os.environ or + "PATH_INFO" not in os.environ): + return False + + scheme, host_port, path, query, fragment = urlparse.urlsplit(url) + + if (host_port == os.environ['HTTP_HOST'] and + urllib2.unquote(path) == urllib2.unquote(os.environ['PATH_INFO'])): + return True + + return False + + +def fetch(url, payload=None, method=GET, headers={}, allow_truncated=False, + follow_redirects=True): """Fetches the given HTTP URL, blocking until the result is returned. Other optional parameters are: @@ -161,8 +191,15 @@ payload: POST or PUT payload (implies method is not GET, HEAD, or DELETE) headers: dictionary of HTTP headers to send with the request allow_truncated: if true, truncate large responses and return them without - error. otherwise, ResponseTooLargeError will be thrown when a response is - truncated. + error. otherwise, ResponseTooLargeError will be thrown when a response is + truncated. + follow_redirects: if true (the default), redirects are + transparently followed and the response (if less than 5 + redirects) contains the final destination's payload and the + response status is 200. You lose, however, the redirect chain + information. If false, you see the HTTP response yourself, + including the 'Location' header, and redirects are not + followed. We use a HTTP/1.1 compliant proxy to fetch the result. @@ -177,15 +214,20 @@ of the returned structure, so HTTP errors like 404 do not result in an exception. """ - request = urlfetch_service_pb.URLFetchRequest() - response = urlfetch_service_pb.URLFetchResponse() - request.set_url(url) - if isinstance(method, basestring): method = method.upper() method = _URL_STRING_MAP.get(method, method) if method not in _VALID_METHODS: raise InvalidMethodError('Invalid method %s.' % str(method)) + + if _is_fetching_self(url, method): + raise InvalidURLError("App cannot fetch the same URL as the one used for " + "the request.") + + request = urlfetch_service_pb.URLFetchRequest() + response = urlfetch_service_pb.URLFetchResponse() + request.set_url(url) + if method == GET: request.set_method(urlfetch_service_pb.URLFetchRequest.GET) elif method == POST: @@ -205,6 +247,8 @@ header_proto.set_key(key) header_proto.set_value(value) + request.set_followredirects(follow_redirects) + try: apiproxy_stub_map.MakeSyncCall('urlfetch', 'Fetch', request, response) except apiproxy_errors.ApplicationError, e: