racoon 0.3.1 → 0.3.2
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 +12 -6
- data/bin/{apnsend → racoon-send} +21 -20
- data/lib/racoon.rb +4 -0
- data/lib/racoon/client.rb +4 -2
- data/lib/racoon/server.rb +34 -23
- metadata +12 -11
data/README.mdown
CHANGED
@@ -34,14 +34,14 @@ You can see progress by looking at the [issue tracker](https://www.pivotaltracke
|
|
34
34
|
## Preparing Certificates
|
35
35
|
|
36
36
|
Certificates must be prepared before they can be used with racoon. Unfortunately, Apple
|
37
|
-
gives us
|
37
|
+
gives us *.p12* files instead of *.pem* files. As such, we need to convert them. This
|
38
38
|
can be accomplished by dropping to the command line and running this command:
|
39
39
|
|
40
40
|
<pre>
|
41
41
|
$ openssl pkcs12 -in cert.p12 -out cert.pem -nodes -clcerts
|
42
42
|
</pre>
|
43
43
|
|
44
|
-
This will generate a file suitable for use with this daemon, called
|
44
|
+
This will generate a file suitable for use with this daemon, called *cert.pem*. If you're
|
45
45
|
using frac.as, this is the file you would upload to the web service.
|
46
46
|
|
47
47
|
If you're not using frac.as, then the contents of this file are what you need to use as
|
@@ -80,7 +80,7 @@ the following method:
|
|
80
80
|
def beanstalk
|
81
81
|
return @beanstalk if @beanstalk
|
82
82
|
@beanstalk = Beanstalk::Pool.new ["127.0.0.1:11300"]
|
83
|
-
@beanstalk.use "
|
83
|
+
@beanstalk.use "racoon-apns"
|
84
84
|
@beanstalk
|
85
85
|
end
|
86
86
|
```
|
@@ -90,6 +90,9 @@ the appropriate tube whether the connection is open yet or not.
|
|
90
90
|
|
91
91
|
We will also need two pieces of information: A project, and a notification.
|
92
92
|
|
93
|
+
**Note that the tube should always be "racoon-apns". The server won't look on any other tube for
|
94
|
+
push notifications.**
|
95
|
+
|
93
96
|
### Project
|
94
97
|
|
95
98
|
A project os comprised of a few pieces of information at a minimum (you may supply more if you
|
@@ -105,8 +108,7 @@ A notification is a ruby hash containing the things to be sent along, including
|
|
105
108
|
An example notification may look like this:
|
106
109
|
|
107
110
|
```ruby
|
108
|
-
notification = { :
|
109
|
-
:aps => { :alert => "Some text",
|
111
|
+
notification = { :aps => { :alert => "Some text",
|
110
112
|
:sound => "Audio_file",
|
111
113
|
:badge => 42,
|
112
114
|
:custom => { :field => "lala",
|
@@ -118,7 +120,11 @@ notification = { :device_token => "hex encoded device token",
|
|
118
120
|
Finally within we can send a push notification using the following code:
|
119
121
|
|
120
122
|
```ruby
|
121
|
-
beanstalk.yput({ :project => project,
|
123
|
+
beanstalk.yput({ :project => project,
|
124
|
+
:notification => notification,
|
125
|
+
:device_token => "binary encoded token",
|
126
|
+
:sandbox => true
|
127
|
+
})
|
122
128
|
```
|
123
129
|
|
124
130
|
Note that the `sandbox` parameter is used to indicate whether or not we're using a development
|
data/bin/{apnsend → racoon-send}
RENAMED
@@ -4,16 +4,17 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
4
4
|
require 'logger'
|
5
5
|
require 'getoptlong'
|
6
6
|
require 'rubygems'
|
7
|
-
require '
|
7
|
+
require 'racoon'
|
8
8
|
require 'base64'
|
9
9
|
require 'socket'
|
10
|
+
require 'yajl'
|
11
|
+
require 'csv'
|
12
|
+
require 'beanstalk-client'
|
10
13
|
|
11
14
|
def usage
|
12
|
-
puts "Usage:
|
13
|
-
puts " --
|
14
|
-
puts " --port <2195> the port of the apn server"
|
15
|
+
puts "Usage: racoon-send [switches] (--b64-token | --hex-token) <token>"
|
16
|
+
puts " --beanstalk <127.0.0.1:11300> csv of ip:port for beanstalk servers"
|
15
17
|
puts " --pem <path> the path to the pem file, if a pem is supplied the server defaults to gateway.push.apple.com:2195"
|
16
|
-
puts " --pem-passphrase <passphrase> the pem passphrase"
|
17
18
|
puts " --alert <message> the message to send"
|
18
19
|
puts " --sound <default> the sound to play, defaults to 'default'"
|
19
20
|
puts " --badge <number> the badge number"
|
@@ -24,34 +25,30 @@ def usage
|
|
24
25
|
end
|
25
26
|
|
26
27
|
opts = GetoptLong.new(
|
27
|
-
["--
|
28
|
-
["--port", "-p", GetoptLong::REQUIRED_ARGUMENT],
|
28
|
+
["--beanstalk", "-b", GetoptLong::REQUIRED_ARGUMENT],
|
29
29
|
["--pem", "-c", GetoptLong::REQUIRED_ARGUMENT],
|
30
|
-
["--pem-passphrase", "-C", GetoptLong::REQUIRED_ARGUMENT],
|
31
30
|
["--alert", "-a", GetoptLong::REQUIRED_ARGUMENT],
|
32
31
|
["--sound", "-S", GetoptLong::REQUIRED_ARGUMENT],
|
33
|
-
["--badge", "-
|
32
|
+
["--badge", "-n", GetoptLong::REQUIRED_ARGUMENT],
|
34
33
|
["--custom", "-j", GetoptLong::REQUIRED_ARGUMENT],
|
35
34
|
["--b64-token", "-B", GetoptLong::REQUIRED_ARGUMENT],
|
36
35
|
["--hex-token", "-H", GetoptLong::REQUIRED_ARGUMENT],
|
37
36
|
["--help", "-h", GetoptLong::NO_ARGUMENT]
|
38
37
|
)
|
39
38
|
|
40
|
-
|
39
|
+
beanstalks = ["127.0.0.1:11300"]
|
40
|
+
certificate = nil
|
41
|
+
notification = Racoon::Notification.new
|
41
42
|
|
42
43
|
opts.each do |opt, arg|
|
43
44
|
case opt
|
44
45
|
when '--help'
|
45
46
|
usage
|
46
47
|
exit
|
47
|
-
when '--
|
48
|
-
|
49
|
-
when '--port'
|
50
|
-
ApnServer::Config.port = arg.to_i
|
48
|
+
when '--beanstalk'
|
49
|
+
beanstalks = CSV.parse(arg)[0]
|
51
50
|
when '--pem'
|
52
|
-
|
53
|
-
when '--pem-passphrase'
|
54
|
-
ApnServer::Config.password = arg
|
51
|
+
certificate = File.read(arg)
|
55
52
|
when '--alert'
|
56
53
|
notification.alert = arg
|
57
54
|
when '--sound'
|
@@ -59,11 +56,11 @@ opts.each do |opt, arg|
|
|
59
56
|
when '--badge'
|
60
57
|
notification.badge = arg.to_i
|
61
58
|
when '--custom'
|
62
|
-
notification.custom =
|
59
|
+
notification.custom = Yajl::Parser.parse(arg)
|
63
60
|
when '--b64-token'
|
64
61
|
notification.device_token = Base64::decode64(arg)
|
65
62
|
when '--hex-token'
|
66
|
-
notification.device_token
|
63
|
+
notification.device_token = arg.scan(/[0-9a-f][0-9a-f]/).map {|s| s.hex.chr}.join
|
67
64
|
end
|
68
65
|
end
|
69
66
|
|
@@ -71,5 +68,9 @@ if notification.device_token.nil?
|
|
71
68
|
usage
|
72
69
|
exit
|
73
70
|
else
|
74
|
-
|
71
|
+
bs = Beanstalk::Pool.new beanstalks
|
72
|
+
%w{use watch}.each { |s| bs.send(s, "racoon-apns") }
|
73
|
+
bs.ignore("default")
|
74
|
+
project = { :name => "test", :certificate => certificate }
|
75
|
+
bs.yput({ :project => project, :notification => notification.payload, :device_token => notification.device_token, :sandbox => true })
|
75
76
|
end
|
data/lib/racoon.rb
CHANGED
data/lib/racoon/client.rb
CHANGED
@@ -31,7 +31,9 @@ module Racoon
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def write(notification)
|
34
|
-
|
34
|
+
if host.include? "sandbox"
|
35
|
+
Config.logger.debug "#{Time.now} [#{host}:#{port}] Device: #{notification.device_token.unpack('H*')} sending #{notification.json_payload}"
|
36
|
+
end
|
35
37
|
@ssl.write(notification.to_bytes)
|
36
38
|
end
|
37
39
|
|
@@ -39,4 +41,4 @@ module Racoon
|
|
39
41
|
@ssl
|
40
42
|
end
|
41
43
|
end
|
42
|
-
end
|
44
|
+
end
|
data/lib/racoon/server.rb
CHANGED
@@ -15,8 +15,6 @@ module Racoon
|
|
15
15
|
tube = "racoon-#{arg}"
|
16
16
|
return @beanstalks[tube] if @beanstalks[tube]
|
17
17
|
@beanstalks[tube] = Beanstalk::Pool.new @beanstalkd_uris
|
18
|
-
%w{watch use}.each { |s| @beanstalks[tube].send(s, "racoon-#{tube}") }
|
19
|
-
@beanstalks[tube].ignore('default')
|
20
18
|
@beanstalks[tube]
|
21
19
|
end
|
22
20
|
|
@@ -24,8 +22,11 @@ module Racoon
|
|
24
22
|
EventMachine::run do
|
25
23
|
EventMachine::PeriodicTimer.new(3600) do
|
26
24
|
begin
|
27
|
-
|
28
|
-
|
25
|
+
b = beanstalk('feedback')
|
26
|
+
%w{watch use}.each { |s| b.send(s, "racoon-feedback") }
|
27
|
+
b.ignore('default')
|
28
|
+
if b.peek_ready
|
29
|
+
item = b.reserve(1)
|
29
30
|
handle_feedback(item)
|
30
31
|
end
|
31
32
|
rescue Beanstalk::TimedOut
|
@@ -33,21 +34,29 @@ module Racoon
|
|
33
34
|
end
|
34
35
|
end
|
35
36
|
|
37
|
+
# Every minute,poll all the clients, ensuring they've been inactive for 20+ minutes.
|
36
38
|
EventMachine::PeriodicTimer.new(60) do
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
39
|
+
remove_clients = []
|
40
|
+
|
41
|
+
@clients.each_pair do |project_name, packet|
|
42
|
+
if Time.now - packet[:timestamp] >= 1200 # 20 minutes
|
43
|
+
packet[:connection].disconnect!
|
44
|
+
remove_clients << project_name
|
41
45
|
end
|
42
|
-
|
43
|
-
|
46
|
+
end
|
47
|
+
|
48
|
+
remove_clients.each do |project_name|
|
49
|
+
@clients[project_name] = nil
|
44
50
|
end
|
45
51
|
end
|
46
52
|
|
47
53
|
EventMachine::PeriodicTimer.new(1) do
|
48
54
|
begin
|
49
|
-
|
50
|
-
|
55
|
+
b = beanstalk('apns')
|
56
|
+
%w{watch use}.each { |s| b.send(s, "racoon-apns") }
|
57
|
+
b.ignore('default')
|
58
|
+
if b.peek_ready
|
59
|
+
item = b.reserve(1)
|
51
60
|
handle_job item
|
52
61
|
end
|
53
62
|
rescue Beanstalk::TimedOut
|
@@ -84,15 +93,17 @@ module Racoon
|
|
84
93
|
|
85
94
|
if notification
|
86
95
|
client = get_client(project[:name], project[:certificate], packet[:sandbox])
|
96
|
+
connection = client[:connection]
|
87
97
|
begin
|
88
|
-
|
89
|
-
|
98
|
+
connection.connect! unless connection.connected?
|
99
|
+
connection.write(notification)
|
100
|
+
@clients[project[:name]][:timestamp] = Time.now
|
90
101
|
|
91
102
|
job.delete
|
92
103
|
rescue Errno::EPIPE, OpenSSL::SSL::SSLError, Errno::ECONNRESET
|
93
104
|
Config.logger.error "Caught Error, closing connecting and adding notification back to queue"
|
94
105
|
|
95
|
-
|
106
|
+
connection.disconnect!
|
96
107
|
|
97
108
|
# Queue back up the notification
|
98
109
|
job.release
|
@@ -127,22 +138,22 @@ module Racoon
|
|
127
138
|
end
|
128
139
|
end
|
129
140
|
|
141
|
+
# Returns a hash containing a timestamp referring to when the connection was opened.
|
142
|
+
# This timestamp will be updated to reflect when there was last activity over the socket.
|
130
143
|
def get_client(project_name, certificate, sandbox = false)
|
131
144
|
uri = "gateway.#{sandbox ? 'sandbox.' : ''}push.apple.com"
|
132
145
|
unless @clients[project_name]
|
133
|
-
@clients[project_name] = Racoon::Client.new(certificate, uri)
|
134
|
-
# in 18 hours (64800 seconds) we need to schedule this socket to be killed. Long opened
|
135
|
-
# sockets don't work.
|
136
|
-
beanstalk('killer').yput({:certificate => certificate, :sandbox => sandbox}, 65536, 64800)
|
146
|
+
@clients[project_name] = { :timestamp => Time.now, :connection => Racoon::Client.new(certificate, uri) }
|
137
147
|
end
|
138
|
-
@clients[project_name] ||= Racoon::Client.new(certificate, uri)
|
148
|
+
@clients[project_name] ||= { :timestamp => Time.now, :connection => Racoon::Client.new(certificate, uri) }
|
139
149
|
client = @clients[project_name]
|
150
|
+
connection = client[:connection]
|
140
151
|
|
141
152
|
# If the certificate has changed, but we still are connected using the old certificate,
|
142
153
|
# disconnect and reconnect.
|
143
|
-
unless
|
144
|
-
|
145
|
-
@clients[project_name] = Racoon::Client.new(certificate, uri)
|
154
|
+
unless connection.pem.eql?(certificate)
|
155
|
+
connection.disconnect! if connection.connected?
|
156
|
+
@clients[project_name] = { :timestamp => Time.now, :connection => Racoon::Client.new(certificate, uri) }
|
146
157
|
client = @clients[project_name]
|
147
158
|
end
|
148
159
|
|
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.3.
|
4
|
+
version: 0.3.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,12 +9,12 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-04-
|
12
|
+
date: 2011-04-24 00:00:00.000000000 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: yajl-ruby
|
17
|
-
requirement: &
|
17
|
+
requirement: &70102541072720 !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: *70102541072720
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: beanstalk-client
|
28
|
-
requirement: &
|
28
|
+
requirement: &70102541072160 !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: *70102541072160
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
38
|
name: bundler
|
39
|
-
requirement: &
|
39
|
+
requirement: &70102541071700 !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: *70102541071700
|
48
48
|
- !ruby/object:Gem::Dependency
|
49
49
|
name: eventmachine
|
50
|
-
requirement: &
|
50
|
+
requirement: &70102541071120 !ruby/object:Gem::Requirement
|
51
51
|
none: false
|
52
52
|
requirements:
|
53
53
|
- - ! '>='
|
@@ -55,19 +55,20 @@ dependencies:
|
|
55
55
|
version: 0.12.8
|
56
56
|
type: :development
|
57
57
|
prerelease: false
|
58
|
-
version_requirements: *
|
58
|
+
version_requirements: *70102541071120
|
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
|
62
62
|
executables:
|
63
|
+
- racoon-send
|
63
64
|
- racoond
|
64
65
|
extensions: []
|
65
66
|
extra_rdoc_files:
|
66
67
|
- README.mdown
|
67
68
|
files:
|
68
|
-
- bin/apnsend
|
69
69
|
- bin/apnserverd.fedora.init
|
70
70
|
- bin/apnserverd.ubuntu.init
|
71
|
+
- bin/racoon-send
|
71
72
|
- bin/racoond
|
72
73
|
- lib/racoon/client.rb
|
73
74
|
- lib/racoon/config.rb
|