Skip to content

Commit a3323a0

Browse files
committed
Merge pull request #1 from jrconlin/master
Merge 0.3.4 to prod
2 parents d8cc30e + 4464fb9 commit a3323a0

File tree

4 files changed

+59
-15
lines changed

4 files changed

+59
-15
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 0.3.4 (2016-05-17)
2+
bug: make header keys case insenstive
3+
4+
## 0.3.3 (2016-05-17)
5+
bug: force key string encoding to utf8
6+
17
## 0.3.2 (2016-04-28)
28
bug: fix setup.py issues
39

pywebpush/__init__.py

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,33 @@ class WebPushException(Exception):
1414
pass
1515

1616

17+
class CaseInsensitiveDict(dict):
18+
"""A dictionary that has case-insensitive keys"""
19+
20+
def __init__(self, data={}, **kwargs):
21+
for key in data:
22+
dict.__setitem__(self, key.lower(), data[key])
23+
self.update(kwargs)
24+
25+
def __contains__(self, key):
26+
return dict.__contains__(self, key.lower())
27+
28+
def __setitem__(self, key, value):
29+
dict.__setitem__(self, key.lower(), value)
30+
31+
def __getitem__(self, key):
32+
return dict.__getitem__(self, key.lower())
33+
34+
def __delitem__(self, key):
35+
dict.__delitem__(self, key.lower())
36+
37+
def get(self, key, default=None):
38+
try:
39+
return self.__getitem__(key)
40+
except KeyError:
41+
return default
42+
43+
1744
class WebPusher:
1845
"""WebPusher encrypts a data block using HTTP Encrypted Content Encoding
1946
for WebPush.
@@ -68,11 +95,13 @@ def __init__(self, subscription_info):
6895
for k in ['p256dh', 'auth']:
6996
if keys.get(k) is None:
7097
raise WebPushException("Missing keys value: %s", k)
71-
receiver_raw = base64.urlsafe_b64decode(self._repad(keys['p256dh']))
98+
receiver_raw = base64.urlsafe_b64decode(
99+
self._repad(keys['p256dh'].encode('utf8')))
72100
if len(receiver_raw) != 65 and receiver_raw[0] != "\x04":
73101
raise WebPushException("Invalid p256dh key specified")
74102
self.receiver_key = receiver_raw
75-
self.auth_key = base64.urlsafe_b64decode(self._repad(keys['auth']))
103+
self.auth_key = base64.urlsafe_b64decode(
104+
self._repad(keys['auth'].encode('utf8')))
76105

77106
def _repad(self, str):
78107
"""Add base64 padding to the end of a string, if required"""
@@ -96,7 +125,7 @@ def encode(self, data):
96125
server_key_id = base64.urlsafe_b64encode(server_key.get_pubkey()[1:])
97126

98127
# http_ece requires that these both be set BEFORE encrypt or
99-
# decrypt is called.
128+
# decrypt is called if you specify the key as "dh".
100129
http_ece.keys[server_key_id] = server_key
101130
http_ece.labels[server_key_id] = "P-256"
102131

@@ -107,12 +136,12 @@ def encode(self, data):
107136
dh=self.receiver_key,
108137
authSecret=self.auth_key)
109138

110-
return {
139+
return CaseInsensitiveDict({
111140
'crypto_key': base64.urlsafe_b64encode(
112141
server_key.get_pubkey()).strip('='),
113142
'salt': base64.urlsafe_b64encode(salt).strip("="),
114143
'body': encrypted,
115-
}
144+
})
116145

117146
def send(self, data, headers={}, ttl=0):
118147
"""Encode and send the data to the Push Service.
@@ -127,6 +156,7 @@ def send(self, data, headers={}, ttl=0):
127156
# Encode the data.
128157
encoded = self.encode(data)
129158
# Append the p256dh to the end of any existing crypto-key
159+
headers = CaseInsensitiveDict(headers)
130160
crypto_key = headers.get("crypto-key", "")
131161
if crypto_key:
132162
crypto_key += ','

pywebpush/tests/test_webpush.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from nose.tools import eq_, ok_
88
import pyelliptic
99

10-
from pywebpush import WebPusher, WebPushException
10+
from pywebpush import WebPusher, WebPushException, CaseInsensitiveDict
1111

1212

1313
class WebpushTestCase(unittest.TestCase):
@@ -24,11 +24,11 @@ def _gen_subscription_info(self, recv_key):
2424
def test_init(self):
2525
# use static values so we know what to look for in the reply
2626
subscription_info = {
27-
"endpoint": "https://example.com/",
28-
"keys": {
29-
"p256dh": ("BOrnIslXrUow2VAzKCUAE4sIbK00daEZCswOcf8m3T"
30-
"F8V82B-OpOg5JbmYLg44kRcvQC1E2gMJshsUYA-_zMPR8"),
31-
"auth": "k8JV6sjdbhAi1n3_LDBLvA"
27+
u"endpoint": u"https://example.com/",
28+
u"keys": {
29+
u"p256dh": (u"BOrnIslXrUow2VAzKCUAE4sIbK00daEZCswOcf8m3T"
30+
"F8V82B-OpOg5JbmYLg44kRcvQC1E2gMJshsUYA-_zMPR8"),
31+
u"auth": u"k8JV6sjdbhAi1n3_LDBLvA"
3232
}
3333
}
3434
self.assertRaises(
@@ -94,15 +94,23 @@ def test_encode(self):
9494
def test_send(self, mock_post):
9595
recv_key = pyelliptic.ECC(curve="prime256v1")
9696
subscription_info = self._gen_subscription_info(recv_key)
97-
headers = {"crypto-key": "pre-existing",
98-
"authentication": "bearer vapid"}
97+
headers = {"Crypto-Key": "pre-existing",
98+
"Authentication": "bearer vapid"}
9999
data = "Mary had a little lamb"
100100
WebPusher(subscription_info).send(data, headers)
101101
eq_(subscription_info.get('endpoint'), mock_post.call_args[0][0])
102102
pheaders = mock_post.call_args[1].get('headers')
103103
eq_(pheaders.get('ttl'), 0)
104104
ok_('encryption' in pheaders)
105-
eq_(pheaders.get('authentication'), headers.get('authentication'))
105+
eq_(pheaders.get('AUTHENTICATION'), headers.get('Authentication'))
106106
ckey = pheaders.get('crypto-key')
107107
ok_('pre-existing,' in ckey)
108108
eq_(pheaders.get('content-encoding'), 'aesgcm')
109+
110+
def test_ci_dict(self):
111+
ci = CaseInsensitiveDict({"Foo": "apple", "bar": "banana"})
112+
eq_('apple', ci["foo"])
113+
eq_('apple', ci.get("FOO"))
114+
eq_('apple', ci.get("Foo"))
115+
del (ci['FOO'])
116+
eq_(None, ci.get('Foo'))

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.2"
6+
__version__ = "0.3.4"
77

88

99
def read_from(file):

0 commit comments

Comments
 (0)