Skip to content

Commit 989ae83

Browse files
committed
Merge pull request #5 from mozilla-services/bug/03
feat: make python2/3 compatible
2 parents 2bdfaaa + 11abb6e commit 989ae83

File tree

6 files changed

+39
-25
lines changed

6 files changed

+39
-25
lines changed

.coveragerc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
[report]
22
omit = *noseplugin*
3+
show_missing = True

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 0.4.0 (2016-06-05)
2+
feat: make python 2.7 / 3.5 polyglot
3+
14
## 0.3.4 (2016-05-17)
25
bug: make header keys case insenstive
36

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ The data can be any serial content (string, bit array, serialized
3636
JSON, etc), but be sure that your receiving application is able to
3737
parse and understand it. (e.g. `data = "Mary had a little lamb."`)
3838

39-
gcm_key is the API key obtained from the Google Developer Console.
40-
It is only needed if endpoint is https://android.googleapis.com/gcm/send
39+
gcm_key is the API key obtained from the Google Developer Console.
40+
It is only needed if endpoint is
41+
https://android.googleapis.com/gcm/send
4142

4243
`headers` is a `dict`ionary of additional HTTP header values (e.g.
4344
[VAPID](https://github.com/mozilla-services/vapid/tree/master/python)
@@ -50,7 +51,6 @@ WebPusher(subscription_info).send(data, headers)
5051
to send for Chrome:
5152
```
5253
WebPusher(subscription_info).send(data, headers, ttl, gcm_key)
53-
```
5454
5555
You can also simply encode the data to send later by calling
5656

pywebpush/__init__.py

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
44

55
import base64
6+
import json
67
import os
78

8-
import json
9+
import six
910
import http_ece
1011
import pyelliptic
1112
import requests
@@ -100,22 +101,22 @@ def __init__(self, subscription_info):
100101
for k in ['p256dh', 'auth']:
101102
if keys.get(k) is None:
102103
raise WebPushException("Missing keys value: %s", k)
103-
receiver_raw = base64.urlsafe_b64decode(
104-
self._repad(keys['p256dh'].encode('utf8')))
104+
if isinstance(keys[k], six.string_types):
105+
keys[k] = bytes(keys[k].encode('utf8'))
106+
receiver_raw = base64.urlsafe_b64decode(self._repad(keys['p256dh']))
105107
if len(receiver_raw) != 65 and receiver_raw[0] != "\x04":
106108
raise WebPushException("Invalid p256dh key specified")
107109
self.receiver_key = receiver_raw
108-
self.auth_key = base64.urlsafe_b64decode(
109-
self._repad(keys['auth'].encode('utf8')))
110+
self.auth_key = base64.urlsafe_b64decode(self._repad(keys['auth']))
110111

111-
def _repad(self, str):
112+
def _repad(self, data):
112113
"""Add base64 padding to the end of a string, if required"""
113-
return str + "===="[:len(str) % 4]
114+
return data + b"===="[:len(data) % 4]
114115

115116
def encode(self, data):
116117
"""Encrypt the data.
117118
118-
:param data: A serialized block of data (String, JSON, bit array,
119+
:param data: A serialized block of byte data (String, JSON, bit array,
119120
etc.) Make sure that whatever you send, your client knows how
120121
to understand it.
121122
@@ -129,6 +130,9 @@ def encode(self, data):
129130
# ID tag.
130131
server_key_id = base64.urlsafe_b64encode(server_key.get_pubkey()[1:])
131132

133+
if isinstance(data, six.string_types):
134+
data = bytes(data.encode('utf8'))
135+
132136
# http_ece requires that these both be set BEFORE encrypt or
133137
# decrypt is called if you specify the key as "dh".
134138
http_ece.keys[server_key_id] = server_key
@@ -143,8 +147,8 @@ def encode(self, data):
143147

144148
return CaseInsensitiveDict({
145149
'crypto_key': base64.urlsafe_b64encode(
146-
server_key.get_pubkey()).strip('='),
147-
'salt': base64.urlsafe_b64encode(salt).strip("="),
150+
server_key.get_pubkey()).strip(b'='),
151+
'salt': base64.urlsafe_b64encode(salt).strip(b'='),
148152
'body': encrypted,
149153
})
150154

@@ -160,6 +164,10 @@ def send(self, data, headers={}, ttl=0, gcm_key=None, reg_id=None):
160164
:param ttl: The Time To Live in seconds for this message if the
161165
recipient is not online. (Defaults to "0", which discards the
162166
message immediately if the recipient is unavailable.)
167+
:param gcm_key: API key obtained from the Google Developer Console.
168+
Needed if endpoint is https://android.googleapis.com/gcm/send
169+
:param reg_id: registration id of the recipient. If not provided,
170+
it will be extracted from the endpoint.
163171
164172
"""
165173
# Encode the data.
@@ -169,11 +177,12 @@ def send(self, data, headers={}, ttl=0, gcm_key=None, reg_id=None):
169177
crypto_key = headers.get("crypto-key", "")
170178
if crypto_key:
171179
crypto_key += ','
172-
crypto_key += "keyid=p256dh;dh=" + encoded["crypto_key"]
180+
crypto_key += "keyid=p256dh;dh=" + encoded["crypto_key"].decode('utf8')
173181
headers.update({
174182
'crypto-key': crypto_key,
175183
'content-encoding': 'aesgcm',
176-
'encryption': "keyid=p256dh;salt=" + encoded['salt'],
184+
'encryption': "keyid=p256dh;salt=" +
185+
encoded['salt'].decode('utf8'),
177186
})
178187
gcm_endpoint = 'https://android.googleapis.com/gcm/send'
179188
if self.subscription_info['endpoint'].startswith(gcm_endpoint):

pywebpush/tests/test_webpush.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ def _gen_subscription_info(self, recv_key, endpoint="https://example.com"):
1616
return {
1717
"endpoint": endpoint,
1818
"keys": {
19-
'auth': base64.urlsafe_b64encode(os.urandom(16)).strip('='),
19+
'auth': base64.urlsafe_b64encode(os.urandom(16)).strip(b'='),
2020
'p256dh': base64.urlsafe_b64encode(
21-
recv_key.get_pubkey()).strip('='),
21+
recv_key.get_pubkey()).strip(b'='),
2222
}
2323
}
2424

@@ -32,6 +32,11 @@ def test_init(self):
3232
u"auth": u"k8JV6sjdbhAi1n3_LDBLvA"
3333
}
3434
}
35+
rk_decode = (b'\x04\xea\xe7"\xc9W\xadJ0\xd9P3(%\x00\x13\x8b'
36+
b'\x08l\xad4u\xa1\x19\n\xcc\x0eq\xff&\xdd1'
37+
b'|W\xcd\x81\xf8\xeaN\x83\x92[\x99\x82\xe0\xe3'
38+
b'\x89\x11r\xf4\x02\xd4M\xa00\x9b!\xb1F\x00'
39+
b'\xfb\xfc\xcc=\x1f')
3540
self.assertRaises(
3641
WebPushException,
3742
WebPusher,
@@ -56,12 +61,8 @@ def test_init(self):
5661

5762
push = WebPusher(subscription_info)
5863
eq_(push.subscription_info, subscription_info)
59-
eq_(push.receiver_key, ('\x04\xea\xe7"\xc9W\xadJ0\xd9P3(%\x00\x13\x8b'
60-
'\x08l\xad4u\xa1\x19\n\xcc\x0eq\xff&\xdd1'
61-
'|W\xcd\x81\xf8\xeaN\x83\x92[\x99\x82\xe0\xe3'
62-
'\x89\x11r\xf4\x02\xd4M\xa00\x9b!\xb1F\x00'
63-
'\xfb\xfc\xcc=\x1f'))
64-
eq_(push.auth_key, '\x93\xc2U\xea\xc8\xddn\x10"\xd6}\xff,0K\xbc')
64+
eq_(push.receiver_key, rk_decode)
65+
eq_(push.auth_key, b'\x93\xc2U\xea\xc8\xddn\x10"\xd6}\xff,0K\xbc')
6566

6667
def test_encode(self):
6768
recv_key = pyelliptic.ECC(curve="prime256v1")
@@ -89,7 +90,7 @@ def test_encode(self):
8990
authSecret=raw_auth
9091
)
9192

92-
eq_(decoded, data)
93+
eq_(decoded.decode('utf8'), data)
9394

9495
@patch("requests.post")
9596
def test_send(self, mock_post):

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from setuptools import find_packages, setup
55

6-
__version__ = "0.3.4"
6+
__version__ = "0.4.0"
77

88

99
def read_from(file):

0 commit comments

Comments
 (0)