Skip to content

Commit a4bc029

Browse files
authored
Added minimal support for sending FCM messages in async using HTTP/2 (#870)
* httpx async_send_each prototype * Clean up code and lint * fix: Add extra dependancy for http2 * fix: reset message batch limit to 500 * fix: Add new import to `setup.py`
1 parent 387f11a commit a4bc029

File tree

7 files changed

+599
-25
lines changed

7 files changed

+599
-25
lines changed

firebase_admin/_utils.py

+86
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616

1717
import json
1818
from platform import python_version
19+
from typing import Callable, Optional
1920

2021
import google.auth
2122
import requests
23+
import httpx
2224

2325
import firebase_admin
2426
from firebase_admin import exceptions
@@ -128,6 +130,36 @@ def handle_platform_error_from_requests(error, handle_func=None):
128130

129131
return exc if exc else _handle_func_requests(error, message, error_dict)
130132

133+
def handle_platform_error_from_httpx(
134+
error: httpx.HTTPError,
135+
handle_func: Optional[Callable[..., Optional[exceptions.FirebaseError]]] = None
136+
) -> exceptions.FirebaseError:
137+
"""Constructs a ``FirebaseError`` from the given httpx error.
138+
139+
This can be used to handle errors returned by Google Cloud Platform (GCP) APIs.
140+
141+
Args:
142+
error: An error raised by the httpx module while making an HTTP call to a GCP API.
143+
handle_func: A function that can be used to handle platform errors in a custom way. When
144+
specified, this function will be called with three arguments. It has the same
145+
signature as ```_handle_func_httpx``, but may return ``None``.
146+
147+
Returns:
148+
FirebaseError: A ``FirebaseError`` that can be raised to the user code.
149+
"""
150+
151+
if isinstance(error, httpx.HTTPStatusError):
152+
response = error.response
153+
content = response.content.decode()
154+
status_code = response.status_code
155+
error_dict, message = _parse_platform_error(content, status_code)
156+
exc = None
157+
if handle_func:
158+
exc = handle_func(error, message, error_dict)
159+
160+
return exc if exc else _handle_func_httpx(error, message, error_dict)
161+
return handle_httpx_error(error)
162+
131163

132164
def handle_operation_error(error):
133165
"""Constructs a ``FirebaseError`` from the given operation error.
@@ -204,6 +236,60 @@ def handle_requests_error(error, message=None, code=None):
204236
err_type = _error_code_to_exception_type(code)
205237
return err_type(message=message, cause=error, http_response=error.response)
206238

239+
def _handle_func_httpx(error: httpx.HTTPError, message, error_dict) -> exceptions.FirebaseError:
240+
"""Constructs a ``FirebaseError`` from the given GCP error.
241+
242+
Args:
243+
error: An error raised by the httpx module while making an HTTP call.
244+
message: A message to be included in the resulting ``FirebaseError``.
245+
error_dict: Parsed GCP error response.
246+
247+
Returns:
248+
FirebaseError: A ``FirebaseError`` that can be raised to the user code or None.
249+
"""
250+
code = error_dict.get('status')
251+
return handle_httpx_error(error, message, code)
252+
253+
254+
def handle_httpx_error(error: httpx.HTTPError, message=None, code=None) -> exceptions.FirebaseError:
255+
"""Constructs a ``FirebaseError`` from the given httpx error.
256+
257+
This method is agnostic of the remote service that produced the error, whether it is a GCP
258+
service or otherwise. Therefore, this method does not attempt to parse the error response in
259+
any way.
260+
261+
Args:
262+
error: An error raised by the httpx module while making an HTTP call.
263+
message: A message to be included in the resulting ``FirebaseError`` (optional). If not
264+
specified the string representation of the ``error`` argument is used as the message.
265+
code: A GCP error code that will be used to determine the resulting error type (optional).
266+
If not specified the HTTP status code on the error response is used to determine a
267+
suitable error code.
268+
269+
Returns:
270+
FirebaseError: A ``FirebaseError`` that can be raised to the user code.
271+
"""
272+
if isinstance(error, httpx.TimeoutException):
273+
return exceptions.DeadlineExceededError(
274+
message='Timed out while making an API call: {0}'.format(error),
275+
cause=error)
276+
if isinstance(error, httpx.ConnectError):
277+
return exceptions.UnavailableError(
278+
message='Failed to establish a connection: {0}'.format(error),
279+
cause=error)
280+
if isinstance(error, httpx.HTTPStatusError):
281+
print("printing status error", error)
282+
if not code:
283+
code = _http_status_to_error_code(error.response.status_code)
284+
if not message:
285+
message = str(error)
286+
287+
err_type = _error_code_to_exception_type(code)
288+
return err_type(message=message, cause=error, http_response=error.response)
289+
290+
return exceptions.UnknownError(
291+
message='Unknown error while making a remote service call: {0}'.format(error),
292+
cause=error)
207293

208294
def _http_status_to_error_code(status):
209295
"""Maps an HTTP status to a platform error code."""

0 commit comments

Comments
 (0)