apns-ruby 0.0.2 → 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/README.md +4 -13
- data/lib/apns/connection.rb +31 -13
- metadata +2 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fb5a30cf2461e888a174d71c290a566c23bd41d7
|
4
|
+
data.tar.gz: 0bd5422d9e7656b5937489728528fcc66f11b745
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9e5b3605fdfc3bbd279b93f4ec8348e171a0c5b8be8c371b07a104993d199fbaf8a0580e93e797bbe702dc5b2b4585ac761235aa1fa577ee14e3465d48fa3993
|
7
|
+
data.tar.gz: 7285ff7bf0688969409368e5434bc7246e6bd2e9768f8a15d1422ef27b6c4b6d09414a444d7e9c1ac23a5e2c2e7a1cd621b9491e782daea653145cdd5ec02ccf
|
data/README.md
CHANGED
@@ -4,6 +4,8 @@ This gem sends APNS notifications with proper error handling. Most importantly,
|
|
4
4
|
* Storing notifications in a database, and have a separate process consume them. Worse, only a single consumer is ever allowed.
|
5
5
|
* Opening a new SSL connection for every notification, which typically takes several hundred milliseconds. Imagine how long it would take to send a million notifications.
|
6
6
|
|
7
|
+
As of 2015-03-04, this gem has been powering [PicCollage](http://pic-collage.com/) for more than a year, sending more than 500 notifications per second at peak time, only to be limited by the devices database's throughput.
|
8
|
+
|
7
9
|
Example usage:
|
8
10
|
```
|
9
11
|
pem = path_to_your_pem_file
|
@@ -27,19 +29,8 @@ n1 = APNS::Notification.new(token, alert: 'hello')
|
|
27
29
|
ne = APNS::Notification.new('bogustoken', alert: 'error')
|
28
30
|
n2 = APNS::Notification.new(token, alert: 'world')
|
29
31
|
conn.push([n1, ne, n2])
|
30
|
-
#
|
31
|
-
|
32
|
-
# Wait for Apple to report an error and close the connection on their side, due to
|
33
|
-
# the bogus token in the second notification.
|
34
|
-
# We'll detect this and automatically retry and invoke your error handler.
|
35
|
-
sleep(7)
|
36
|
-
conn.push([APNS::Notification.new(token, alert: 'hello world 0')])
|
37
|
-
sleep(7)
|
38
|
-
conn.push([APNS::Notification.new(token, alert: 'hello world 1')])
|
39
|
-
|
40
|
-
# 'Invalid token: bogustoken' is printed out
|
41
|
-
# We should be receiving each and every successful notification with texts:
|
42
|
-
# 'world', 'hello world 0', and 'hello world 1'.
|
32
|
+
# 'Invalid token: bogustoken' is printed out.
|
33
|
+
# Moreover, we should be receiving each and every successful notification with texts 'hello' and 'world'.
|
43
34
|
```
|
44
35
|
|
45
36
|
A great amount of code in this gem is copied from https://github.com/jpoz/APNS , many thanks to his pioneering work. This work itself is licensed under the MIT license.
|
data/lib/apns/connection.rb
CHANGED
@@ -20,6 +20,14 @@ class Connection
|
|
20
20
|
@host = host
|
21
21
|
@port = port
|
22
22
|
|
23
|
+
# Our current strategy is to read errors emitted by the APNS in a separate
|
24
|
+
# thread spawned when we open a new connection (read_errors method).
|
25
|
+
# If this thread receives an error information from the @ssl, it turns on
|
26
|
+
# the @lock, so that no direct write operation can be performed on the same
|
27
|
+
# Connection instance.
|
28
|
+
#
|
29
|
+
@lock = Mutex.new
|
30
|
+
|
23
31
|
@sock, @ssl = open_connection
|
24
32
|
ObjectSpace.define_finalizer(self, self.class.finalize(@sock, @ssl))
|
25
33
|
end
|
@@ -31,8 +39,8 @@ class Connection
|
|
31
39
|
end
|
32
40
|
|
33
41
|
def push ns
|
34
|
-
# The notification identifier is set to 4 bytes in the APNS protocol.
|
35
|
-
# upon hitting this limit, read for failures and restart the counting again.
|
42
|
+
# The notification identifier is set to 4 bytes in the APNS protocol.
|
43
|
+
# Thus, upon hitting this limit, read for failures and restart the counting again.
|
36
44
|
if @notifications.size + ns.size > MAX_32_BIT - 10
|
37
45
|
code, failed_id = read_failure_info(timeout: 3)
|
38
46
|
if failed_id
|
@@ -48,7 +56,7 @@ class Connection
|
|
48
56
|
n.message_identifier = [@notifications.size].pack('N')
|
49
57
|
@notifications.push(n)
|
50
58
|
}
|
51
|
-
write ns
|
59
|
+
@lock.synchronize{ write ns }
|
52
60
|
end
|
53
61
|
|
54
62
|
|
@@ -57,16 +65,6 @@ class Connection
|
|
57
65
|
def write ns
|
58
66
|
packed = pack_notifications(ns)
|
59
67
|
@ssl.write(packed)
|
60
|
-
rescue Errno::EPIPE, Errno::ECONNRESET, OpenSSL::SSL::SSLError
|
61
|
-
code, failed_id = read_failure_info(timeout: 30)
|
62
|
-
reopen_connection
|
63
|
-
return unless failed_id # there's nothing we can do
|
64
|
-
|
65
|
-
@error_handler.call(code, @notifications.item_at(failed_id))
|
66
|
-
|
67
|
-
@notifications.delete_where_index_less_than(failed_id+1)
|
68
|
-
ns = @notifications.items_from(failed_id+1)
|
69
|
-
retry
|
70
68
|
end
|
71
69
|
|
72
70
|
def pack_notifications notifications
|
@@ -105,9 +103,29 @@ class Connection
|
|
105
103
|
ssl = OpenSSL::SSL::SSLSocket.new(sock,context)
|
106
104
|
ssl.connect
|
107
105
|
|
106
|
+
Thread.new {
|
107
|
+
read_errors ssl
|
108
|
+
}
|
109
|
+
|
108
110
|
return sock, ssl
|
109
111
|
end
|
110
112
|
|
113
|
+
def read_errors ssl
|
114
|
+
tuple = ssl.read(6)
|
115
|
+
|
116
|
+
@lock.synchronize {
|
117
|
+
_, code, failed_id = tuple.unpack("ccN")
|
118
|
+
reopen_connection
|
119
|
+
return unless failed_id # there's nothing we can do
|
120
|
+
|
121
|
+
@error_handler.call(code, @notifications.item_at(failed_id))
|
122
|
+
|
123
|
+
@notifications.delete_where_index_less_than(failed_id+1)
|
124
|
+
ns = @notifications.items_from(failed_id+1)
|
125
|
+
write ns
|
126
|
+
}
|
127
|
+
end
|
128
|
+
|
111
129
|
# Override inspect since we do not want to print out the entire @notifications,
|
112
130
|
# whose size might be over tens of thousands
|
113
131
|
def inspect
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: apns-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- awaw
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-03-04 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: APNS notifications sent properly with correct connection management and
|
14
14
|
error handling
|
@@ -47,4 +47,3 @@ signing_key:
|
|
47
47
|
specification_version: 4
|
48
48
|
summary: APNS notifications sent properly
|
49
49
|
test_files: []
|
50
|
-
has_rdoc:
|