185 return True |
184 return True |
186 |
185 |
187 return False |
186 return False |
188 |
187 |
189 |
188 |
190 def __create_rpc(deadline=None, callback=None): |
189 def create_rpc(deadline=None, callback=None): |
191 """DO NOT USE. WILL CHANGE AND BREAK YOUR CODE. |
190 """Creates an RPC object for use with the urlfetch API. |
192 |
|
193 Creates an RPC object for use with the urlfetch API. |
|
194 |
191 |
195 Args: |
192 Args: |
196 deadline: deadline in seconds for the operation. |
193 deadline: Optional deadline in seconds for the operation; the default |
197 callback: callable to invoke on completion. |
194 is a system-specific deadline (typically 5 seconds). |
|
195 callback: Optional callable to invoke on completion. |
198 |
196 |
199 Returns: |
197 Returns: |
200 A _URLFetchRPC object. |
198 An apiproxy_stub_map.UserRPC object specialized for this service. |
201 """ |
199 """ |
202 return _URLFetchRPC(deadline, callback) |
200 return apiproxy_stub_map.UserRPC('urlfetch', deadline, callback) |
203 |
201 |
204 |
202 |
205 def fetch(url, payload=None, method=GET, headers={}, allow_truncated=False, |
203 def fetch(url, payload=None, method=GET, headers={}, |
206 follow_redirects=True, deadline=None): |
204 allow_truncated=False, follow_redirects=True, |
|
205 deadline=None): |
207 """Fetches the given HTTP URL, blocking until the result is returned. |
206 """Fetches the given HTTP URL, blocking until the result is returned. |
208 |
207 |
209 Other optional parameters are: |
208 Other optional parameters are: |
210 method: GET, POST, HEAD, PUT, or DELETE |
209 method: GET, POST, HEAD, PUT, or DELETE |
211 payload: POST or PUT payload (implies method is not GET, HEAD, or DELETE). |
210 payload: POST or PUT payload (implies method is not GET, HEAD, or DELETE). |
212 this is ignored if the method is not POST or PUT. |
211 this is ignored if the method is not POST or PUT. |
213 headers: dictionary of HTTP headers to send with the request |
212 headers: dictionary of HTTP headers to send with the request |
214 allow_truncated: if true, truncate large responses and return them without |
213 allow_truncated: if true, truncate large responses and return them without |
215 error. otherwise, ResponseTooLargeError will be thrown when a response is |
214 error. Otherwise, ResponseTooLargeError is raised when a response is |
216 truncated. |
215 truncated. |
217 follow_redirects: if true (the default), redirects are |
216 follow_redirects: if true (the default), redirects are |
218 transparently followed and the response (if less than 5 |
217 transparently followed and the response (if less than 5 |
219 redirects) contains the final destination's payload and the |
218 redirects) contains the final destination's payload and the |
220 response status is 200. You lose, however, the redirect chain |
219 response status is 200. You lose, however, the redirect chain |
234 urlfetch.InvalidURLError. If the server cannot be contacted, we throw a |
233 urlfetch.InvalidURLError. If the server cannot be contacted, we throw a |
235 urlfetch.DownloadError. Note that HTTP errors are returned as a part |
234 urlfetch.DownloadError. Note that HTTP errors are returned as a part |
236 of the returned structure, so HTTP errors like 404 do not result in an |
235 of the returned structure, so HTTP errors like 404 do not result in an |
237 exception. |
236 exception. |
238 """ |
237 """ |
239 rpc = __create_rpc(deadline=deadline) |
238 rpc = create_rpc(deadline=deadline) |
240 rpc.make_call(url, payload, method, headers, follow_redirects) |
239 make_fetch_call(rpc, url, payload, method, headers, |
241 return rpc.get_result(allow_truncated) |
240 allow_truncated, follow_redirects) |
242 |
241 return rpc.get_result() |
243 |
242 |
244 class _URLFetchRPC(object): |
243 |
245 """A RPC object that manages the urlfetch RPC. |
244 def make_fetch_call(rpc, url, payload=None, method=GET, headers={}, |
246 |
245 allow_truncated=False, follow_redirects=True): |
247 Its primary functions are the following: |
246 """Executes the RPC call to fetch a given HTTP URL. |
248 1. Convert error codes to the URLFetchServiceError namespace and raise them |
247 |
249 when get_result is called. |
248 The first argument is a UserRPC instance. See urlfetch.fetch for a |
250 2. Wrap the urlfetch response with a _URLFetchResult object. |
249 thorough description of remaining arguments. |
251 """ |
250 """ |
252 |
251 assert rpc.service == 'urlfetch', repr(rpc.service) |
253 def __init__(self, deadline=None, callback=None): |
252 if isinstance(method, basestring): |
254 """Construct a new url fetch RPC. |
253 method = method.upper() |
255 |
254 method = _URL_STRING_MAP.get(method, method) |
256 Args: |
255 if method not in _VALID_METHODS: |
257 deadline: deadline in seconds for the operation. |
256 raise InvalidMethodError('Invalid method %s.' % str(method)) |
258 callback: callable to invoke on completion. |
257 |
259 """ |
258 if _is_fetching_self(url, method): |
260 self.__rpc = apiproxy_stub_map.CreateRPC('urlfetch') |
259 raise InvalidURLError("App cannot fetch the same URL as the one used for " |
261 self.__rpc.deadline = deadline |
260 "the request.") |
262 self.__rpc.callback = callback |
261 |
263 self.__called_hooks = False |
262 request = urlfetch_service_pb.URLFetchRequest() |
264 |
263 response = urlfetch_service_pb.URLFetchResponse() |
265 def make_call(self, url, payload=None, method=GET, headers={}, |
264 request.set_url(url) |
266 follow_redirects=True): |
265 |
267 """Executes the RPC call to fetch a given HTTP URL. |
266 if method == GET: |
268 |
267 request.set_method(urlfetch_service_pb.URLFetchRequest.GET) |
269 See urlfetch.fetch for a thorough description of arguments. |
268 elif method == POST: |
270 """ |
269 request.set_method(urlfetch_service_pb.URLFetchRequest.POST) |
271 assert self.__rpc.state is apiproxy_rpc.RPC.IDLE |
270 elif method == HEAD: |
272 if isinstance(method, basestring): |
271 request.set_method(urlfetch_service_pb.URLFetchRequest.HEAD) |
273 method = method.upper() |
272 elif method == PUT: |
274 method = _URL_STRING_MAP.get(method, method) |
273 request.set_method(urlfetch_service_pb.URLFetchRequest.PUT) |
275 if method not in _VALID_METHODS: |
274 elif method == DELETE: |
276 raise InvalidMethodError('Invalid method %s.' % str(method)) |
275 request.set_method(urlfetch_service_pb.URLFetchRequest.DELETE) |
277 |
276 |
278 if _is_fetching_self(url, method): |
277 if payload and (method == POST or method == PUT): |
279 raise InvalidURLError("App cannot fetch the same URL as the one used for " |
278 request.set_payload(payload) |
280 "the request.") |
279 |
281 |
280 for key, value in headers.iteritems(): |
282 self.__request = urlfetch_service_pb.URLFetchRequest() |
281 header_proto = request.add_header() |
283 self.__response = urlfetch_service_pb.URLFetchResponse() |
282 header_proto.set_key(key) |
284 self.__result = None |
283 header_proto.set_value(str(value)) |
285 self.__request.set_url(url) |
284 |
286 |
285 request.set_followredirects(follow_redirects) |
287 if method == GET: |
286 |
288 self.__request.set_method(urlfetch_service_pb.URLFetchRequest.GET) |
287 if rpc.deadline is not None: |
289 elif method == POST: |
288 request.set_deadline(rpc.deadline) |
290 self.__request.set_method(urlfetch_service_pb.URLFetchRequest.POST) |
289 |
291 elif method == HEAD: |
290 rpc.make_call('Fetch', request, response, _get_fetch_result, allow_truncated) |
292 self.__request.set_method(urlfetch_service_pb.URLFetchRequest.HEAD) |
291 |
293 elif method == PUT: |
292 |
294 self.__request.set_method(urlfetch_service_pb.URLFetchRequest.PUT) |
293 def _get_fetch_result(rpc): |
295 elif method == DELETE: |
294 """Check success, handle exceptions, and return converted RPC result. |
296 self.__request.set_method(urlfetch_service_pb.URLFetchRequest.DELETE) |
295 |
297 |
296 This method waits for the RPC if it has not yet finished, and calls the |
298 if payload and (method == POST or method == PUT): |
297 post-call hooks on the first invocation. |
299 self.__request.set_payload(payload) |
298 |
300 |
299 Args: |
301 for key, value in headers.iteritems(): |
300 rpc: A UserRPC object. |
302 header_proto = self.__request.add_header() |
301 |
303 header_proto.set_key(key) |
302 Raises: |
304 header_proto.set_value(str(value)) |
303 InvalidURLError if the url was invalid. |
305 |
304 DownloadError if there was a problem fetching the url. |
306 self.__request.set_followredirects(follow_redirects) |
305 ResponseTooLargeError if the response was either truncated (and |
307 if self.__rpc.deadline: |
306 allow_truncated=False was passed to make_fetch_call()), or if it |
308 self.__request.set_deadline(self.__rpc.deadline) |
307 was too big for us to download. |
309 |
308 |
310 apiproxy_stub_map.apiproxy.GetPreCallHooks().Call( |
309 Returns: |
311 'urlfetch', 'Fetch', self.__request, self.__response) |
310 A _URLFetchResult object. |
312 self.__rpc.MakeCall('urlfetch', 'Fetch', self.__request, self.__response) |
311 """ |
313 |
312 assert rpc.service == 'urlfetch', repr(rpc.service) |
314 def wait(self): |
313 assert rpc.method == 'Fetch', repr(rpc.method) |
315 """Waits for the urlfetch RPC to finish. Idempotent. |
314 try: |
316 """ |
315 rpc.check_success() |
317 assert self.__rpc.state is not apiproxy_rpc.RPC.IDLE |
316 except apiproxy_errors.ApplicationError, err: |
318 if self.__rpc.state is apiproxy_rpc.RPC.RUNNING: |
317 if (err.application_error == |
319 self.__rpc.Wait() |
318 urlfetch_service_pb.URLFetchServiceError.INVALID_URL): |
320 |
319 raise InvalidURLError(str(err)) |
321 def check_success(self, allow_truncated=False): |
320 if (err.application_error == |
322 """Check success and convert RPC exceptions to urlfetch exceptions. |
321 urlfetch_service_pb.URLFetchServiceError.UNSPECIFIED_ERROR): |
323 |
322 raise DownloadError(str(err)) |
324 This method waits for the RPC if it has not yet finished, and calls the |
323 if (err.application_error == |
325 post-call hooks on the first invocation. |
324 urlfetch_service_pb.URLFetchServiceError.FETCH_ERROR): |
326 |
325 raise DownloadError(str(err)) |
327 Args: |
326 if (err.application_error == |
328 allow_truncated: if False, an error is raised if the response was |
327 urlfetch_service_pb.URLFetchServiceError.RESPONSE_TOO_LARGE): |
329 truncated. |
328 raise ResponseTooLargeError(None) |
330 |
329 if (err.application_error == |
331 Raises: |
330 urlfetch_service_pb.URLFetchServiceError.DEADLINE_EXCEEDED): |
332 InvalidURLError if the url was invalid. |
331 raise DownloadError(str(err)) |
333 DownloadError if there was a problem fetching the url. |
332 raise err |
334 ResponseTooLargeError if the response was either truncated (and |
333 |
335 allow_truncated is false) or if it was too big for us to download. |
334 response = rpc.response |
336 """ |
335 allow_truncated = rpc.user_data |
337 assert self.__rpc.state is not apiproxy_rpc.RPC.IDLE |
336 result = _URLFetchResult(response) |
338 if self.__rpc.state is apiproxy_rpc.RPC.RUNNING: |
337 if response.contentwastruncated() and not allow_truncated: |
339 self.wait() |
338 raise ResponseTooLargeError(result) |
340 |
339 return result |
341 try: |
|
342 self.__rpc.CheckSuccess() |
|
343 if not self.__called_hooks: |
|
344 self.__called_hooks = True |
|
345 apiproxy_stub_map.apiproxy.GetPostCallHooks().Call( |
|
346 'urlfetch', 'Fetch', self.__request, self.__response) |
|
347 except apiproxy_errors.ApplicationError, e: |
|
348 if (e.application_error == |
|
349 urlfetch_service_pb.URLFetchServiceError.INVALID_URL): |
|
350 raise InvalidURLError(str(e)) |
|
351 if (e.application_error == |
|
352 urlfetch_service_pb.URLFetchServiceError.UNSPECIFIED_ERROR): |
|
353 raise DownloadError(str(e)) |
|
354 if (e.application_error == |
|
355 urlfetch_service_pb.URLFetchServiceError.FETCH_ERROR): |
|
356 raise DownloadError(str(e)) |
|
357 if (e.application_error == |
|
358 urlfetch_service_pb.URLFetchServiceError.RESPONSE_TOO_LARGE): |
|
359 raise ResponseTooLargeError(None) |
|
360 if (e.application_error == |
|
361 urlfetch_service_pb.URLFetchServiceError.DEADLINE_EXCEEDED): |
|
362 raise DownloadError(str(e)) |
|
363 raise e |
|
364 |
|
365 if self.__response.contentwastruncated() and not allow_truncated: |
|
366 raise ResponseTooLargeError(_URLFetchResult(self.__response)) |
|
367 |
|
368 def get_result(self, allow_truncated=False): |
|
369 """Returns the RPC result or raises an exception if the rpc failed. |
|
370 |
|
371 This method waits for the RPC if not completed, and checks success. |
|
372 |
|
373 Args: |
|
374 allow_truncated: if False, an error is raised if the response was |
|
375 truncated. |
|
376 |
|
377 Returns: |
|
378 The urlfetch result. |
|
379 |
|
380 Raises: |
|
381 Error if the rpc has not yet finished. |
|
382 InvalidURLError if the url was invalid. |
|
383 DownloadError if there was a problem fetching the url. |
|
384 ResponseTooLargeError if the response was either truncated (and |
|
385 allow_truncated is false) or if it was too big for us to download. |
|
386 """ |
|
387 if self.__result is None: |
|
388 self.check_success(allow_truncated) |
|
389 self.__result = _URLFetchResult(self.__response) |
|
390 return self.__result |
|
391 |
340 |
392 |
341 |
393 Fetch = fetch |
342 Fetch = fetch |
394 |
343 |
395 |
344 |