apns-ruby 0.0.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|