webpush 0.3.8 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +30 -0
- data/.travis.yml +10 -4
- data/CHANGELOG.md +7 -0
- data/README.md +1 -0
- data/Rakefile +2 -2
- data/lib/webpush.rb +23 -12
- data/lib/webpush/encryption.rb +23 -40
- data/lib/webpush/errors.rb +3 -2
- data/lib/webpush/railtie.rb +2 -0
- data/lib/webpush/request.rb +54 -53
- data/lib/webpush/vapid_key.rb +4 -2
- data/lib/webpush/version.rb +3 -1
- data/webpush.gemspec +15 -16
- metadata +11 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5fbb0e5a0419639817533b0ce1b45c0b222fafa5
|
4
|
+
data.tar.gz: b3f72a12240ef2496c27aefc7a88a896fd1f2aa4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0ef773e38114e36f2d903ffcb892ffde10e5ca837908e76df1f2c95e31da9044f80e6add328e769ec70ddd8a43dcfcaff0122298346903dda8a730962e8e7f4c
|
7
|
+
data.tar.gz: 7e242909ab6e783f81a7553a193e384aa8b956537087de172838e50199d7dd37c7c78204248932b5574022c0624ef3ff7d6bc730f1e65b6f03858a993a7582e6
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require: rubocop-performance
|
2
|
+
|
3
|
+
AllCops:
|
4
|
+
Exclude:
|
5
|
+
- 'bin/**/*'
|
6
|
+
|
7
|
+
Metrics/AbcSize:
|
8
|
+
Max: 20
|
9
|
+
|
10
|
+
Metrics/ClassLength:
|
11
|
+
Max: 100
|
12
|
+
|
13
|
+
Metrics/ModuleLength:
|
14
|
+
Max: 100
|
15
|
+
|
16
|
+
Metrics/LineLength:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
Metrics/BlockLength:
|
20
|
+
Exclude:
|
21
|
+
- spec/**/*_spec.rb
|
22
|
+
|
23
|
+
Lint/AmbiguousBlockAssociation:
|
24
|
+
Enabled: false
|
25
|
+
|
26
|
+
Style/Documentation:
|
27
|
+
Enabled: false
|
28
|
+
|
29
|
+
Style/IndentHeredoc:
|
30
|
+
Enabled: false
|
data/.travis.yml
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
env:
|
2
|
+
global:
|
3
|
+
- CC_TEST_REPORTER_ID=155202524386dfebe0c3267a5c868b5417ff4cc2cde8ed301fb36b177d46a458
|
1
4
|
language: ruby
|
2
5
|
rvm:
|
3
6
|
- 2.2
|
@@ -6,12 +9,15 @@ rvm:
|
|
6
9
|
- 2.5
|
7
10
|
- 2.6
|
8
11
|
before_install:
|
9
|
-
-
|
10
|
-
if [[ ${RUBY_VERSION} =~ ^ruby-2.3 ]]; then
|
11
|
-
gem uninstall -i /home/travis/.rvm/gems/${RUBY_VERSION}@global bundler -x
|
12
|
-
fi
|
12
|
+
- gem uninstall -v '>= 2' -i $(rvm gemdir)@global -ax bundler || true
|
13
13
|
- gem install bundler -v 1.17.3
|
14
|
+
before_script:
|
15
|
+
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
16
|
+
- chmod +x ./cc-test-reporter
|
17
|
+
- ./cc-test-reporter before-build
|
14
18
|
script: "bundle exec rake spec"
|
19
|
+
after_script:
|
20
|
+
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
15
21
|
addons:
|
16
22
|
code_climate:
|
17
23
|
repo_token: 155202524386dfebe0c3267a5c868b5417ff4cc2cde8ed301fb36b177d46a458
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## [v0.3.8](https://github.com/zaru/webpush/tree/v0.3.8) (2019-04-16)
|
4
|
+
[Full Changelog](https://github.com/zaru/webpush/compare/v0.3.7...v0.3.8)
|
5
|
+
|
6
|
+
**Merged pull requests:**
|
7
|
+
|
8
|
+
- Fix authorization header [\#72](https://github.com/zaru/webpush/pull/72) ([xronos-i-am](https://github.com/xronos-i-am))
|
9
|
+
|
3
10
|
## [v0.3.7](https://github.com/zaru/webpush/tree/v0.3.7) (2019-03-06)
|
4
11
|
[Full Changelog](https://github.com/zaru/webpush/compare/v0.3.6...v0.3.7)
|
5
12
|
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# WebPush
|
2
2
|
|
3
3
|
[](https://codeclimate.com/github/zaru/webpush)
|
4
|
+
[](https://codeclimate.com/github/zaru/webpush/coverage)
|
4
5
|
[](https://travis-ci.org/zaru/webpush)
|
5
6
|
[](https://badge.fury.io/rb/webpush)
|
6
7
|
|
data/Rakefile
CHANGED
data/lib/webpush.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'openssl'
|
2
4
|
require 'base64'
|
3
5
|
require 'hkdf'
|
@@ -11,6 +13,10 @@ require 'webpush/encryption'
|
|
11
13
|
require 'webpush/request'
|
12
14
|
require 'webpush/railtie' if defined?(Rails)
|
13
15
|
|
16
|
+
# Push API implementation
|
17
|
+
#
|
18
|
+
# https://tools.ietf.org/html/rfc8030
|
19
|
+
# https://www.w3.org/TR/push-api/
|
14
20
|
module Webpush
|
15
21
|
class << self
|
16
22
|
# Deliver the payload to the required endpoint given by the JavaScript
|
@@ -28,21 +34,16 @@ module Webpush
|
|
28
34
|
# @param options [Hash<Symbol,String>] additional options for the notification
|
29
35
|
# @option options [#to_s] :ttl Time-to-live in seconds
|
30
36
|
# @option options [#to_s] :urgency Urgency can be very-low, low, normal, high
|
31
|
-
|
32
|
-
|
33
|
-
endpoint: endpoint,
|
34
|
-
keys: {
|
35
|
-
p256dh: p256dh,
|
36
|
-
auth: auth
|
37
|
-
}
|
38
|
-
}
|
37
|
+
# rubocop:disable Metrics/ParameterLists
|
38
|
+
def payload_send(message: '', endpoint:, p256dh: '', auth: '', vapid: {}, **options)
|
39
39
|
Webpush::Request.new(
|
40
40
|
message: message,
|
41
|
-
subscription: subscription,
|
41
|
+
subscription: subscription(endpoint, p256dh, auth),
|
42
42
|
vapid: vapid,
|
43
43
|
**options
|
44
44
|
).perform
|
45
45
|
end
|
46
|
+
# rubocop:enable Metrics/ParameterLists
|
46
47
|
|
47
48
|
# Generate a VapidKey instance to obtain base64 encoded public and private keys
|
48
49
|
# suitable for VAPID protocol JSON web token signing
|
@@ -59,11 +60,21 @@ module Webpush
|
|
59
60
|
def decode64(str)
|
60
61
|
# For Ruby < 2.3, Base64.urlsafe_decode64 strict decodes and will raise errors if encoded value is not properly padded
|
61
62
|
# Implementation: http://ruby-doc.org/stdlib-2.3.0/libdoc/base64/rdoc/Base64.html#method-i-urlsafe_decode64
|
62
|
-
if !str.end_with?(
|
63
|
-
str = str.ljust((str.length + 3) & ~3, "=")
|
64
|
-
end
|
63
|
+
str = str.ljust((str.length + 3) & ~3, '=') if !str.end_with?('=') && str.length % 4 != 0
|
65
64
|
|
66
65
|
Base64.urlsafe_decode64(str)
|
67
66
|
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def subscription(endpoint, p256dh, auth)
|
71
|
+
{
|
72
|
+
endpoint: endpoint,
|
73
|
+
keys: {
|
74
|
+
p256dh: p256dh,
|
75
|
+
auth: auth
|
76
|
+
}
|
77
|
+
}
|
78
|
+
end
|
68
79
|
end
|
69
80
|
end
|
data/lib/webpush/encryption.rb
CHANGED
@@ -1,11 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Webpush
|
2
4
|
module Encryption
|
3
5
|
extend self
|
4
6
|
|
7
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
5
8
|
def encrypt(message, p256dh, auth)
|
6
9
|
assert_arguments(message, p256dh, auth)
|
7
10
|
|
8
|
-
group_name =
|
11
|
+
group_name = 'prime256v1'
|
9
12
|
salt = Random.new.bytes(16)
|
10
13
|
|
11
14
|
server = OpenSSL::PKey::EC.new(group_name)
|
@@ -20,75 +23,55 @@ module Webpush
|
|
20
23
|
|
21
24
|
client_auth_token = Webpush.decode64(auth)
|
22
25
|
|
23
|
-
|
26
|
+
info = "WebPush: info\0" + client_public_key_bn.to_s(2) + server_public_key_bn.to_s(2)
|
27
|
+
content_encryption_key_info = "Content-Encoding: aes128gcm\0"
|
28
|
+
nonce_info = "Content-Encoding: nonce\0"
|
24
29
|
|
25
|
-
|
30
|
+
prk = HKDF.new(shared_secret, salt: client_auth_token, algorithm: 'SHA256', info: info).next_bytes(32)
|
26
31
|
|
27
|
-
content_encryption_key_info = create_info('aesgcm', context)
|
28
32
|
content_encryption_key = HKDF.new(prk, salt: salt, info: content_encryption_key_info).next_bytes(16)
|
29
33
|
|
30
|
-
nonce_info = create_info('nonce', context)
|
31
34
|
nonce = HKDF.new(prk, salt: salt, info: nonce_info).next_bytes(12)
|
32
35
|
|
33
36
|
ciphertext = encrypt_payload(message, content_encryption_key, nonce)
|
34
37
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
server_public_key_bn: convert16bit(server_public_key_bn),
|
39
|
-
server_public_key: server_public_key_bn.to_s(2),
|
40
|
-
shared_secret: shared_secret
|
41
|
-
}
|
42
|
-
end
|
38
|
+
serverkey16bn = convert16bit(server_public_key_bn)
|
39
|
+
rs = ciphertext.bytesize
|
40
|
+
raise ArgumentError, "encrypted payload is too big" if rs > 4096
|
43
41
|
|
44
|
-
|
42
|
+
aes128gcmheader = "#{salt}" + [rs].pack('N*') + [serverkey16bn.bytesize].pack('C*') + serverkey16bn
|
45
43
|
|
46
|
-
|
47
|
-
c = convert16bit(client_public_key)
|
48
|
-
s = convert16bit(server_public_key)
|
49
|
-
context = "\0"
|
50
|
-
context += [c.bytesize].pack("n*")
|
51
|
-
context += c
|
52
|
-
context += [s.bytesize].pack("n*")
|
53
|
-
context += s
|
54
|
-
context
|
44
|
+
aes128gcmheader + ciphertext
|
55
45
|
end
|
46
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
47
|
+
|
48
|
+
private
|
56
49
|
|
57
50
|
def encrypt_payload(plaintext, content_encryption_key, nonce)
|
58
51
|
cipher = OpenSSL::Cipher.new('aes-128-gcm')
|
59
52
|
cipher.encrypt
|
60
53
|
cipher.key = content_encryption_key
|
61
54
|
cipher.iv = nonce
|
62
|
-
padding = cipher.update("\0\0")
|
63
55
|
text = cipher.update(plaintext)
|
64
|
-
|
65
|
-
e_text =
|
56
|
+
padding = cipher.update("\2\0")
|
57
|
+
e_text = text + padding + cipher.final
|
66
58
|
e_tag = cipher.auth_tag
|
67
59
|
|
68
60
|
e_text + e_tag
|
69
61
|
end
|
70
62
|
|
71
|
-
def create_info(type, context)
|
72
|
-
info = "Content-Encoding: "
|
73
|
-
info += type
|
74
|
-
info += "\0"
|
75
|
-
info += "P-256"
|
76
|
-
info += context
|
77
|
-
info
|
78
|
-
end
|
79
|
-
|
80
63
|
def convert16bit(key)
|
81
|
-
[key.to_s(16)].pack(
|
64
|
+
[key.to_s(16)].pack('H*')
|
82
65
|
end
|
83
66
|
|
84
67
|
def assert_arguments(message, p256dh, auth)
|
85
|
-
raise ArgumentError,
|
86
|
-
raise ArgumentError,
|
87
|
-
raise ArgumentError,
|
68
|
+
raise ArgumentError, 'message cannot be blank' if blank?(message)
|
69
|
+
raise ArgumentError, 'p256dh cannot be blank' if blank?(p256dh)
|
70
|
+
raise ArgumentError, 'auth cannot be blank' if blank?(auth)
|
88
71
|
end
|
89
72
|
|
90
73
|
def blank?(value)
|
91
74
|
value.nil? || value.empty?
|
92
75
|
end
|
93
76
|
end
|
94
|
-
end
|
77
|
+
end
|
data/lib/webpush/errors.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Webpush
|
2
4
|
class Error < RuntimeError; end
|
3
5
|
|
4
6
|
class ConfigurationError < Error; end
|
5
7
|
|
6
|
-
class ResponseError < Error
|
8
|
+
class ResponseError < Error
|
7
9
|
attr_reader :response, :host
|
8
10
|
|
9
11
|
def initialize(response, host)
|
@@ -24,5 +26,4 @@ module Webpush
|
|
24
26
|
class TooManyRequests < ResponseError; end
|
25
27
|
|
26
28
|
class PushServiceError < ResponseError; end
|
27
|
-
|
28
29
|
end
|
data/lib/webpush/railtie.rb
CHANGED
data/lib/webpush/request.rb
CHANGED
@@ -1,13 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'jwt'
|
2
4
|
require 'base64'
|
3
5
|
|
4
6
|
module Webpush
|
5
7
|
# It is temporary URL until supported by the GCM server.
|
6
|
-
GCM_URL = 'https://android.googleapis.com/gcm/send'
|
7
|
-
TEMP_GCM_URL = 'https://
|
8
|
+
GCM_URL = 'https://android.googleapis.com/gcm/send'.freeze
|
9
|
+
TEMP_GCM_URL = 'https://fcm.googleapis.com/fcm'.freeze
|
8
10
|
|
11
|
+
# rubocop:disable Metrics/ClassLength
|
9
12
|
class Request
|
10
|
-
def initialize(message:
|
13
|
+
def initialize(message: '', subscription:, vapid:, **options)
|
11
14
|
endpoint = subscription.fetch(:endpoint)
|
12
15
|
@endpoint = endpoint.gsub(GCM_URL, TEMP_GCM_URL)
|
13
16
|
@payload = build_payload(message, subscription)
|
@@ -15,6 +18,7 @@ module Webpush
|
|
15
18
|
@options = default_options.merge(options)
|
16
19
|
end
|
17
20
|
|
21
|
+
# rubocop:disable Metrics/AbcSize
|
18
22
|
def perform
|
19
23
|
http = Net::HTTP.new(uri.host, uri.port)
|
20
24
|
http.use_ssl = true
|
@@ -25,63 +29,46 @@ module Webpush
|
|
25
29
|
req = Net::HTTP::Post.new(uri.request_uri, headers)
|
26
30
|
req.body = body
|
27
31
|
resp = http.request(req)
|
28
|
-
|
29
|
-
if resp.is_a?(Net::HTTPGone) # 410
|
30
|
-
raise ExpiredSubscription.new(resp, uri.host)
|
31
|
-
elsif resp.is_a?(Net::HTTPNotFound) # 404
|
32
|
-
raise InvalidSubscription.new(resp, uri.host)
|
33
|
-
elsif resp.is_a?(Net::HTTPUnauthorized) || resp.is_a?(Net::HTTPForbidden) || # 401, 403
|
34
|
-
resp.is_a?(Net::HTTPBadRequest) && resp.message == "UnauthorizedRegistration" # 400, Google FCM
|
35
|
-
raise Unauthorized.new(resp, uri.host)
|
36
|
-
elsif resp.is_a?(Net::HTTPRequestEntityTooLarge) # 413
|
37
|
-
raise PayloadTooLarge.new(resp, uri.host)
|
38
|
-
elsif resp.is_a?(Net::HTTPTooManyRequests) # 429, try again later!
|
39
|
-
raise TooManyRequests.new(resp, uri.host)
|
40
|
-
elsif resp.is_a?(Net::HTTPServerError) # 5xx
|
41
|
-
raise PushServiceError.new(resp, uri.host)
|
42
|
-
elsif !resp.is_a?(Net::HTTPSuccess) # unknown/unhandled response error
|
43
|
-
raise ResponseError.new(resp, uri.host)
|
44
|
-
end
|
32
|
+
verify_response(resp)
|
45
33
|
|
46
34
|
resp
|
47
35
|
end
|
36
|
+
# rubocop:enable Metrics/AbcSize
|
48
37
|
|
38
|
+
# rubocop:disable Metrics/MethodLength
|
49
39
|
def headers
|
50
40
|
headers = {}
|
51
|
-
headers[
|
52
|
-
headers[
|
53
|
-
headers[
|
54
|
-
|
55
|
-
if @payload
|
56
|
-
headers[
|
57
|
-
headers["
|
58
|
-
headers["Crypto-Key"] = "dh=#{dh_param}"
|
41
|
+
headers['Content-Type'] = 'application/octet-stream'
|
42
|
+
headers['Ttl'] = ttl
|
43
|
+
headers['Urgency'] = urgency
|
44
|
+
|
45
|
+
if @payload
|
46
|
+
headers['Content-Encoding'] = 'aes128gcm'
|
47
|
+
headers["Content-Length"] = @payload.length.to_s
|
59
48
|
end
|
60
49
|
|
61
50
|
if api_key?
|
62
|
-
headers[
|
51
|
+
headers['Authorization'] = "key=#{api_key}"
|
63
52
|
elsif vapid?
|
64
|
-
|
65
|
-
headers["Authorization"] = vapid_headers["Authorization"]
|
66
|
-
headers["Crypto-Key"] = [ headers["Crypto-Key"], vapid_headers["Crypto-Key"] ].compact.join(";")
|
53
|
+
headers["Authorization"] = build_vapid_header
|
67
54
|
end
|
68
55
|
|
69
56
|
headers
|
70
57
|
end
|
58
|
+
# rubocop:enable Metrics/MethodLength
|
59
|
+
|
60
|
+
def build_vapid_header
|
61
|
+
# https://tools.ietf.org/id/draft-ietf-webpush-vapid-03.html
|
71
62
|
|
72
|
-
def build_vapid_headers
|
73
63
|
vapid_key = vapid_pem ? VapidKey.from_pem(vapid_pem) : VapidKey.from_keys(vapid_public_key, vapid_private_key)
|
74
64
|
jwt = JWT.encode(jwt_payload, vapid_key.curve, 'ES256', jwt_header_fields)
|
75
65
|
p256ecdsa = vapid_key.public_key_for_push_header
|
76
66
|
|
77
|
-
|
78
|
-
'Authorization' => 'WebPush ' + jwt,
|
79
|
-
'Crypto-Key' => 'p256ecdsa=' + p256ecdsa,
|
80
|
-
}
|
67
|
+
"vapid t=#{jwt},k=#{p256ecdsa}"
|
81
68
|
end
|
82
69
|
|
83
70
|
def body
|
84
|
-
@payload
|
71
|
+
@payload || ''
|
85
72
|
end
|
86
73
|
|
87
74
|
private
|
@@ -98,32 +85,24 @@ module Webpush
|
|
98
85
|
@options.fetch(:urgency).to_s
|
99
86
|
end
|
100
87
|
|
101
|
-
def dh_param
|
102
|
-
trim_encode64(@payload.fetch(:server_public_key))
|
103
|
-
end
|
104
|
-
|
105
|
-
def salt_param
|
106
|
-
trim_encode64(@payload.fetch(:salt))
|
107
|
-
end
|
108
|
-
|
109
88
|
def jwt_payload
|
110
89
|
{
|
111
90
|
aud: audience,
|
112
91
|
exp: Time.now.to_i + expiration,
|
113
|
-
sub: subject
|
92
|
+
sub: subject
|
114
93
|
}
|
115
94
|
end
|
116
95
|
|
117
96
|
def jwt_header_fields
|
118
|
-
{
|
97
|
+
{ "typ": "JWT", "alg": "ES256" }
|
119
98
|
end
|
120
99
|
|
121
100
|
def audience
|
122
|
-
uri.scheme +
|
101
|
+
uri.scheme + '://' + uri.host
|
123
102
|
end
|
124
103
|
|
125
104
|
def expiration
|
126
|
-
@vapid_options.fetch(:expiration, 24*60*60)
|
105
|
+
@vapid_options.fetch(:expiration, 24 * 60 * 60)
|
127
106
|
end
|
128
107
|
|
129
108
|
def subject
|
@@ -144,13 +123,13 @@ module Webpush
|
|
144
123
|
|
145
124
|
def default_options
|
146
125
|
{
|
147
|
-
ttl: 60*60*24*7*4, # 4 weeks
|
126
|
+
ttl: 60 * 60 * 24 * 7 * 4, # 4 weeks
|
148
127
|
urgency: 'normal'
|
149
128
|
}
|
150
129
|
end
|
151
130
|
|
152
131
|
def build_payload(message, subscription)
|
153
|
-
return
|
132
|
+
return nil if message.nil? || message.empty?
|
154
133
|
|
155
134
|
encrypt_payload(message, subscription.fetch(:keys))
|
156
135
|
end
|
@@ -164,7 +143,7 @@ module Webpush
|
|
164
143
|
end
|
165
144
|
|
166
145
|
def api_key?
|
167
|
-
!(api_key.nil? || api_key.empty?) && @endpoint =~
|
146
|
+
!(api_key.nil? || api_key.empty?) && @endpoint =~ %r{\Ahttps://(android|gcm-http|fcm)\.googleapis\.com}
|
168
147
|
end
|
169
148
|
|
170
149
|
def vapid?
|
@@ -174,5 +153,27 @@ module Webpush
|
|
174
153
|
def trim_encode64(bin)
|
175
154
|
Webpush.encode64(bin).delete('=')
|
176
155
|
end
|
156
|
+
|
157
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Style/GuardClause
|
158
|
+
def verify_response(resp)
|
159
|
+
if resp.is_a?(Net::HTTPGone) # 410
|
160
|
+
raise ExpiredSubscription.new(resp, uri.host)
|
161
|
+
elsif resp.is_a?(Net::HTTPNotFound) # 404
|
162
|
+
raise InvalidSubscription.new(resp, uri.host)
|
163
|
+
elsif resp.is_a?(Net::HTTPUnauthorized) || resp.is_a?(Net::HTTPForbidden) || # 401, 403
|
164
|
+
resp.is_a?(Net::HTTPBadRequest) && resp.message == 'UnauthorizedRegistration' # 400, Google FCM
|
165
|
+
raise Unauthorized.new(resp, uri.host)
|
166
|
+
elsif resp.is_a?(Net::HTTPRequestEntityTooLarge) # 413
|
167
|
+
raise PayloadTooLarge.new(resp, uri.host)
|
168
|
+
elsif resp.is_a?(Net::HTTPTooManyRequests) # 429, try again later!
|
169
|
+
raise TooManyRequests.new(resp, uri.host)
|
170
|
+
elsif resp.is_a?(Net::HTTPServerError) # 5xx
|
171
|
+
raise PushServiceError.new(resp, uri.host)
|
172
|
+
elsif !resp.is_a?(Net::HTTPSuccess) # unknown/unhandled response error
|
173
|
+
raise ResponseError.new(resp, uri.host)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Style/GuardClause
|
177
177
|
end
|
178
|
+
# rubocop:enable Metrics/ClassLength
|
178
179
|
end
|
data/lib/webpush/vapid_key.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Webpush
|
2
4
|
# Class for abstracting the generation and encoding of elliptic curve public and private keys for use with the VAPID protocol
|
3
5
|
#
|
@@ -78,12 +80,12 @@ module Webpush
|
|
78
80
|
def to_pem
|
79
81
|
public_key = OpenSSL::PKey::EC.new curve
|
80
82
|
public_key.private_key = nil
|
81
|
-
|
83
|
+
|
82
84
|
curve.to_pem + public_key.to_pem
|
83
85
|
end
|
84
86
|
|
85
87
|
def inspect
|
86
|
-
"#<#{self.class}:#{object_id.to_s(16)} #{to_h.map { |k, v| ":#{k}=#{v}" }.join(
|
88
|
+
"#<#{self.class}:#{object_id.to_s(16)} #{to_h.map { |k, v| ":#{k}=#{v}" }.join(' ')}>"
|
87
89
|
end
|
88
90
|
|
89
91
|
private
|
data/lib/webpush/version.rb
CHANGED
data/webpush.gemspec
CHANGED
@@ -1,29 +1,28 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
3
|
require 'webpush/version'
|
5
4
|
|
6
5
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
6
|
+
spec.name = 'webpush'
|
8
7
|
spec.version = Webpush::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
8
|
+
spec.authors = ['zaru@sakuraba']
|
9
|
+
spec.email = ['zarutofu@gmail.com']
|
11
10
|
|
12
|
-
spec.summary =
|
13
|
-
spec.homepage =
|
11
|
+
spec.summary = 'Encryption Utilities for Web Push payload. '
|
12
|
+
spec.homepage = 'https://github.com/zaru/webpush'
|
14
13
|
|
15
14
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
16
|
-
spec.bindir =
|
15
|
+
spec.bindir = 'exe'
|
17
16
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
18
|
-
spec.require_paths = [
|
17
|
+
spec.require_paths = ['lib']
|
19
18
|
|
20
|
-
spec.add_dependency
|
21
|
-
spec.add_dependency
|
19
|
+
spec.add_dependency 'hkdf', '~> 0.2'
|
20
|
+
spec.add_dependency 'jwt', '~> 2.0'
|
22
21
|
|
23
|
-
spec.add_development_dependency
|
22
|
+
spec.add_development_dependency 'bundler', '~> 1.17.3'
|
24
23
|
spec.add_development_dependency 'pry'
|
25
|
-
spec.add_development_dependency
|
26
|
-
spec.add_development_dependency
|
27
|
-
spec.add_development_dependency
|
28
|
-
spec.add_development_dependency
|
24
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
25
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
26
|
+
spec.add_development_dependency 'simplecov'
|
27
|
+
spec.add_development_dependency 'webmock', '~> 1.24'
|
29
28
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: webpush
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- zaru@sakuraba
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-08-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: hkdf
|
@@ -95,33 +95,33 @@ dependencies:
|
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '3.0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: simplecov
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - "
|
101
|
+
- - ">="
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
103
|
+
version: '0'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- - "
|
108
|
+
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
110
|
+
version: '0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
112
|
+
name: webmock
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
115
|
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: '
|
117
|
+
version: '1.24'
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: '
|
124
|
+
version: '1.24'
|
125
125
|
description:
|
126
126
|
email:
|
127
127
|
- zarutofu@gmail.com
|
@@ -131,6 +131,7 @@ extra_rdoc_files: []
|
|
131
131
|
files:
|
132
132
|
- ".gitignore"
|
133
133
|
- ".rspec"
|
134
|
+
- ".rubocop.yml"
|
134
135
|
- ".travis.yml"
|
135
136
|
- CHANGELOG.md
|
136
137
|
- Gemfile
|