racoon 0.3.2 → 0.4.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.
- data/README.mdown +6 -2
- data/bin/racoon-send +5 -1
- data/lib/racoon.rb +1 -1
- data/lib/racoon/notification.rb +18 -26
- data/lib/racoon/server.rb +52 -30
- metadata +9 -9
data/README.mdown
CHANGED
@@ -28,8 +28,7 @@ persistent connection to Apple.
|
|
28
28
|
|
29
29
|
## Remaining Tasks & Issues
|
30
30
|
|
31
|
-
You can see progress by looking at the [issue tracker](https://www.pivotaltracker.com/projects/
|
32
|
-
*apnserver* or *racoon* are related to this subproject.
|
31
|
+
You can see progress by looking at the [issue tracker](https://www.pivotaltracker.com/projects/279053).
|
33
32
|
|
34
33
|
## Preparing Certificates
|
35
34
|
|
@@ -122,11 +121,16 @@ Finally within we can send a push notification using the following code:
|
|
122
121
|
```ruby
|
123
122
|
beanstalk.yput({ :project => project,
|
124
123
|
:notification => notification,
|
124
|
+
:send_at => Time.mktime(2012, 12, 21, 4, 15)
|
125
125
|
:device_token => "binary encoded token",
|
126
126
|
:sandbox => true
|
127
127
|
})
|
128
128
|
```
|
129
129
|
|
130
|
+
This will construct a notification which is to be scheduled to be delivered on December 21st,
|
131
|
+
2012 at 4:15 AM localtime to the server. That is, if the server's timezone is UTC, you must
|
132
|
+
account for that in your client application.
|
133
|
+
|
130
134
|
Note that the `sandbox` parameter is used to indicate whether or not we're using a development
|
131
135
|
certificate, and as such, should contact Apple's sandbox APN service instead of the production
|
132
136
|
certificate. If left out, we assume production.
|
data/bin/racoon-send
CHANGED
@@ -72,5 +72,9 @@ else
|
|
72
72
|
%w{use watch}.each { |s| bs.send(s, "racoon-apns") }
|
73
73
|
bs.ignore("default")
|
74
74
|
project = { :name => "test", :certificate => certificate }
|
75
|
-
bs.yput({ :project => project,
|
75
|
+
bs.yput({ :project => project,
|
76
|
+
:notification => notification.payload,
|
77
|
+
:device_token => notification.device_token,
|
78
|
+
:sandbox => true
|
79
|
+
})
|
76
80
|
end
|
data/lib/racoon.rb
CHANGED
data/lib/racoon/notification.rb
CHANGED
@@ -6,7 +6,12 @@ module Racoon
|
|
6
6
|
class Notification
|
7
7
|
include Racoon::Payload
|
8
8
|
|
9
|
-
attr_accessor :device_token, :alert, :badge, :sound, :custom
|
9
|
+
attr_accessor :identifier, :expiry, :device_token, :alert, :badge, :sound, :custom, :send_at, :expiry
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@expiry = 0
|
13
|
+
@send_at = Time.now
|
14
|
+
end
|
10
15
|
|
11
16
|
def payload
|
12
17
|
p = Hash.new
|
@@ -23,24 +28,9 @@ module Racoon
|
|
23
28
|
j
|
24
29
|
end
|
25
30
|
|
26
|
-
=begin
|
27
|
-
def push
|
28
|
-
if Config.pem.nil?
|
29
|
-
socket = TCPSocket.new(Config.host || 'localhost', Config.port.to_i || 22195)
|
30
|
-
socket.write(to_bytes)
|
31
|
-
socket.close
|
32
|
-
else
|
33
|
-
client = Racoon::Client.new(Config.pem, Config.host || 'gateway.push.apple.com', Config.port.to_i || 2195)
|
34
|
-
client.connect!
|
35
|
-
client.write(self)
|
36
|
-
client.disconnect!
|
37
|
-
end
|
38
|
-
end
|
39
|
-
=end
|
40
|
-
|
41
31
|
def to_bytes
|
42
32
|
j = json_payload
|
43
|
-
[
|
33
|
+
[1, identifier, expiry.to_i, device_token.size, device_token, j.size, j].pack("cNNna*na*")
|
44
34
|
end
|
45
35
|
|
46
36
|
def self.valid?(p)
|
@@ -62,23 +52,25 @@ module Racoon
|
|
62
52
|
buffer = p.dup
|
63
53
|
notification = Notification.new
|
64
54
|
|
65
|
-
header = buffer.slice!(0,
|
66
|
-
if header[0] !=
|
67
|
-
|
68
|
-
|
55
|
+
header = buffer.slice!(0, 11).unpack("cNNn")
|
56
|
+
raise RuntimeError.new("Header of notification is invalid: #{header.inspect}") if header[0] != 1
|
57
|
+
|
58
|
+
# identifier
|
59
|
+
notification.identifier = header[1]
|
60
|
+
notification.expiry = header[2]
|
69
61
|
|
70
|
-
#
|
71
|
-
notification.device_token = buffer.slice!(0, 32).unpack(
|
62
|
+
# device token
|
63
|
+
notification.device_token = buffer.slice!(0, 32).unpack("a*").first
|
72
64
|
|
73
|
-
#
|
74
|
-
payload_len = buffer.slice!(0, 2).unpack(
|
65
|
+
# JSON payload
|
66
|
+
payload_len = buffer.slice!(0, 2).unpack("n")
|
75
67
|
j = buffer.slice!(0, payload_len.last)
|
76
68
|
result = Yajl::Parser.parse(j)
|
77
69
|
|
78
70
|
['alert', 'badge', 'sound'].each do |k|
|
79
71
|
notification.send("#{k}=", result['aps'][k]) if result['aps'] && result['aps'][k]
|
80
72
|
end
|
81
|
-
result.delete(
|
73
|
+
result.delete("aps")
|
82
74
|
notification.custom = result
|
83
75
|
|
84
76
|
notification
|
data/lib/racoon/server.rb
CHANGED
@@ -50,15 +50,17 @@ module Racoon
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
-
EventMachine::PeriodicTimer.new(
|
53
|
+
EventMachine::PeriodicTimer.new(2) do
|
54
54
|
begin
|
55
55
|
b = beanstalk('apns')
|
56
56
|
%w{watch use}.each { |s| b.send(s, "racoon-apns") }
|
57
57
|
b.ignore('default')
|
58
|
-
|
58
|
+
jobs = []
|
59
|
+
until b.peek_ready.nil?
|
59
60
|
item = b.reserve(1)
|
60
|
-
|
61
|
+
jobs << item
|
61
62
|
end
|
63
|
+
handle_jobs jobs if jobs.count > 0
|
62
64
|
rescue Beanstalk::TimedOut
|
63
65
|
Config.logger.info "(Beanstalkd) Unable to secure a job, operation timed out."
|
64
66
|
end
|
@@ -75,42 +77,47 @@ module Racoon
|
|
75
77
|
# :certificate => "contents of a certificate.pem"
|
76
78
|
# },
|
77
79
|
# :device_token => "0f21ab...def",
|
78
|
-
# :notification => notification.
|
80
|
+
# :notification => notification.payload,
|
79
81
|
# :sandbox => true # Development environment?
|
80
82
|
# }
|
81
|
-
def
|
82
|
-
|
83
|
-
|
83
|
+
def handle_jobs(jobs)
|
84
|
+
connections = {}
|
85
|
+
jobs.each do |job|
|
86
|
+
packet = job.ybody
|
87
|
+
project = packet[:project]
|
84
88
|
|
85
|
-
|
89
|
+
client = get_client(project[:name], project[:certificate], packet[:sandbox])
|
90
|
+
conn = client[:connection]
|
91
|
+
connections[conn] ||= []
|
86
92
|
|
87
|
-
|
88
|
-
notification.device_token = packet[:device_token]
|
89
|
-
notification.badge = aps[:badge] if aps.has_key? :badge
|
90
|
-
notification.alert = aps[:alert] if aps.has_key? :alert
|
91
|
-
notification.sound = aps[:sound] if aps.has_key? :sound
|
92
|
-
notification.custom = aps[:custom] if aps.has_key? :custom
|
93
|
+
notification = create_notification_from_packet(packet)
|
93
94
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
95
|
+
connections[conn] << { :job => job, :notification => notification }
|
96
|
+
end
|
97
|
+
|
98
|
+
connections.each_pair do |conn, tasks|
|
99
|
+
conn.connect! unless conn.connected?
|
100
|
+
tasks.each do |data|
|
101
|
+
job = data[:job]
|
102
|
+
notif = data[:notification]
|
101
103
|
|
102
|
-
|
103
|
-
|
104
|
-
|
104
|
+
begin
|
105
|
+
conn.write(notif)
|
106
|
+
@clients[project[:name]][:timestamp] = Time.now
|
105
107
|
|
106
|
-
|
108
|
+
# TODO: Listen for error responses from Apple
|
109
|
+
job.delete
|
110
|
+
rescue Errno::EPIPE, OpenSSL::SSL::SSLError, Errno::ECONNRESET
|
111
|
+
Config.logger.error "Caught error, closing connection and adding notification back to queue"
|
107
112
|
|
108
|
-
|
109
|
-
job.release
|
110
|
-
rescue RuntimeError => e
|
111
|
-
Config.logger.error "Unable to handle: #{e}"
|
113
|
+
connection.disconnect!
|
112
114
|
|
113
|
-
|
115
|
+
job.release
|
116
|
+
rescue RuntimeError => e
|
117
|
+
Config.logger.error "Unable to handle: #{e}"
|
118
|
+
|
119
|
+
job.delete
|
120
|
+
end
|
114
121
|
end
|
115
122
|
end
|
116
123
|
end
|
@@ -167,5 +174,20 @@ module Racoon
|
|
167
174
|
@clients[project_name] = nil
|
168
175
|
job.delete
|
169
176
|
end
|
177
|
+
|
178
|
+
def create_notification_from_packet(packet)
|
179
|
+
aps = packet[:notification][:aps]
|
180
|
+
|
181
|
+
notification = Notification.new
|
182
|
+
notification.identifier = packet[:identifier]
|
183
|
+
notification.expiry = packet[:expiry]
|
184
|
+
notification.device_token = packet[:device_token]
|
185
|
+
notification.badge = aps[:badge] if aps.has_key? :badge
|
186
|
+
notification.alert = aps[:alert] if aps.has_key? :alert
|
187
|
+
notification.sound = aps[:sound] if aps.has_key? :sound
|
188
|
+
notification.custom = aps[:custom] if aps.has_key? :custom
|
189
|
+
|
190
|
+
notification
|
191
|
+
end
|
170
192
|
end
|
171
193
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: racoon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -14,7 +14,7 @@ default_executable:
|
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: yajl-ruby
|
17
|
-
requirement: &
|
17
|
+
requirement: &70324876524860 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ! '>='
|
@@ -22,10 +22,10 @@ dependencies:
|
|
22
22
|
version: 0.7.0
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *70324876524860
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: beanstalk-client
|
28
|
-
requirement: &
|
28
|
+
requirement: &70324876524360 !ruby/object:Gem::Requirement
|
29
29
|
none: false
|
30
30
|
requirements:
|
31
31
|
- - ! '>='
|
@@ -33,10 +33,10 @@ dependencies:
|
|
33
33
|
version: 1.0.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
|
-
version_requirements: *
|
36
|
+
version_requirements: *70324876524360
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
38
|
name: bundler
|
39
|
-
requirement: &
|
39
|
+
requirement: &70324876523860 !ruby/object:Gem::Requirement
|
40
40
|
none: false
|
41
41
|
requirements:
|
42
42
|
- - ~>
|
@@ -44,10 +44,10 @@ dependencies:
|
|
44
44
|
version: 1.0.0
|
45
45
|
type: :development
|
46
46
|
prerelease: false
|
47
|
-
version_requirements: *
|
47
|
+
version_requirements: *70324876523860
|
48
48
|
- !ruby/object:Gem::Dependency
|
49
49
|
name: eventmachine
|
50
|
-
requirement: &
|
50
|
+
requirement: &70324876523360 !ruby/object:Gem::Requirement
|
51
51
|
none: false
|
52
52
|
requirements:
|
53
53
|
- - ! '>='
|
@@ -55,7 +55,7 @@ dependencies:
|
|
55
55
|
version: 0.12.8
|
56
56
|
type: :development
|
57
57
|
prerelease: false
|
58
|
-
version_requirements: *
|
58
|
+
version_requirements: *70324876523360
|
59
59
|
description: A toolkit for proxying and sending Apple Push Notifications prepared
|
60
60
|
for a hosted environment
|
61
61
|
email: jeremy.tregunna@me.com
|