From 3fe08ad1e61324325d4a2dadb025e80d2834c6f1 Mon Sep 17 00:00:00 2001 From: jrconlin Date: Fri, 30 Mar 2018 10:28:39 -0700 Subject: [PATCH] bug: return the remote server response in the WebpushException Closes #90 --- README.md | 10 +++++++++- README.rst | 10 +++++++++- pywebpush/__init__.py | 32 ++++++++++++++++++++++++++------ pywebpush/tests/test_webpush.py | 31 ++++++++++++++++++++++++++++++- setup.py | 2 +- 5 files changed, 75 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 42fe978..b7499fd 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ in the `subscription_info` block. *data* - can be any serial content (string, bit array, serialized JSON, etc), but be sure that your receiving application is able to parse and understand it. (e.g. `data = "Mary had a little lamb."`) -*content_type* - specifies the form of Encryption to use, either `'aesgcm'` or the newer `'aes128gcm'`. NOTE that +*content_type* - specifies the form of Encryption to use, either `'aesgcm'` or the newer `'aes128gcm'`. NOTE that not all User Agents can decrypt `'aes128gcm'`, so the library defaults to the older form. *vapid_claims* - a `dict` containing the VAPID claims required for authorization (See @@ -103,6 +103,14 @@ try: ) except WebPushException as ex: print("I'm sorry, Dave, but I can't do that: {}", repr(ex)) + # Mozilla returns additional information in the body of the response. + if ex.response and ex.response.json(): + extra = ex.response.json() + print("Remote service replied with a {}:{}, {}", + extra.code, + extra.errno, + extra.message + ) ``` ### Methods diff --git a/README.rst b/README.rst index c9cd67c..7dca032 100644 --- a/README.rst +++ b/README.rst @@ -105,11 +105,19 @@ e.g. the output of: data="Mary had a little lamb, with a nice mint jelly", vapid_private_key="path/to/vapid_private.pem", vapid_claims={ - "sub": "YourNameHere@example.org", + "sub": "mailto:YourNameHere@example.org", } ) except WebPushException as ex: print("I'm sorry, Dave, but I can't do that: {}", repr(ex)) + # Mozilla returns additional information in the body of the response. + if ex.response and ex.response.json(): + extra = ex.response.json() + print("Remote service replied with a {}:{}, {}", + extra.code, + extra.errno, + extra.message + ) Methods ~~~~~~~ diff --git a/pywebpush/__init__.py b/pywebpush/__init__.py index 0311868..1adeee2 100644 --- a/pywebpush/__init__.py +++ b/pywebpush/__init__.py @@ -21,7 +21,26 @@ from py_vapid import Vapid class WebPushException(Exception): - pass + """Web Push failure. + + This may contain the requests.Response + + """ + + def __init__(self, message, response=None): + self.message = message + self.response = response + + def __str__(self): + extra = "" + if self.response: + try: + extra = ", Response {}".format( + self.response.text, + ) + except AttributeError: + extra = ", Response {}".format(self.response) + return "WebPushException: {}{}".format(self.message, extra) class CaseInsensitiveDict(dict): @@ -371,7 +390,7 @@ def webpush(subscription_info, else: vv = Vapid.from_string(private_key=vapid_private_key) vapid_headers = vv.sign(vapid_claims) - result = WebPusher(subscription_info).send( + response = WebPusher(subscription_info).send( data, vapid_headers, ttl=ttl, @@ -379,7 +398,8 @@ def webpush(subscription_info, curl=curl, timeout=timeout, ) - if not curl and result.status_code > 202: - raise WebPushException("Push failed: {}: {}".format( - result, result.text)) - return result + if not curl and response.status_code > 202: + raise WebPushException("Push failed: {} {}".format( + response.status_code, response.reason), + response=response) + return response diff --git a/pywebpush/tests/test_webpush.py b/pywebpush/tests/test_webpush.py index ded6a69..34c623e 100644 --- a/pywebpush/tests/test_webpush.py +++ b/pywebpush/tests/test_webpush.py @@ -3,7 +3,7 @@ import json import os import unittest -from mock import patch +from mock import patch, Mock from nose.tools import eq_, ok_, assert_is_not, assert_raises import http_ece from cryptography.hazmat.primitives.asymmetric import ec @@ -339,3 +339,32 @@ class WebpushTestCase(unittest.TestCase): ckey = pheaders.get('crypto-key') ok_('pre-existing' in ckey) eq_(pheaders.get('content-encoding'), 'aesgcm') + + +class WebpushExceptionTestCase(unittest.TestCase): + + def test_exception(self): + from requests import Response + + exp = WebPushException("foo") + assert ("{}".format(exp) == "WebPushException: foo") + # Really should try to load the response to verify, but this mock + # covers what we need. + response = Mock(spec=Response) + response.text = ( + '{"code": 401, "errno": 109, "error": ' + '"Unauthorized", "more_info": "http://' + 'autopush.readthedocs.io/en/latest/htt' + 'p.html#error-codes", "message": "Requ' + 'est did not validate missing authoriz' + 'ation header"}') + response.json.return_value = json.loads(response.text) + response.status_code = 401 + response.reason = "Unauthorized" + exp = WebPushException("foo", response) + assert "{}".format(exp) == "WebPushException: foo, Response {}".format( + response.text) + assert '{}'.format(exp.response), '' + assert exp.response.json().get('errno') == 109 + exp = WebPushException("foo", [1, 2, 3]) + assert '{}'.format(exp) == "WebPushException: foo, Response [1, 2, 3]" diff --git a/setup.py b/setup.py index 54b2bdc..705206e 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ import os from setuptools import find_packages, setup -__version__ = "1.6.0" +__version__ = "1.7.0" def read_from(file):