racoon 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|