racoon 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 @.p12@ files instead of @.pem@ files. As such, we need to convert them. This
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 @cert.pem@. If you're
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 "awesome-tube"
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 = { :device_token => "hex encoded device token",
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, :notification => notification, :sandbox => true })
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
@@ -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 'apnserver'
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: apnsend [switches] (--b64-token | --hex-token) <token>"
13
- puts " --server <localhost> the apn server defaults to a locally running apnserverd"
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
- ["--server", "-s", GetoptLong::REQUIRED_ARGUMENT],
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", "-b", GetoptLong::REQUIRED_ARGUMENT],
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
- notification = ApnServer::Notification.new
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 '--server'
48
- ApnServer::Config.host = arg
49
- when '--port'
50
- ApnServer::Config.port = arg.to_i
48
+ when '--beanstalk'
49
+ beanstalks = CSV.parse(arg)[0]
51
50
  when '--pem'
52
- ApnServer::Config.pem = File.read(arg)
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 = ActiveSupport::JSON.decode(arg)
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 = arg.scan(/[0-9a-f][0-9a-f]/).map {|s| s.hex.chr}.join
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
- notification.push
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
@@ -4,3 +4,7 @@ require 'racoon/payload'
4
4
  require 'racoon/notification'
5
5
  require 'racoon/client'
6
6
  require 'racoon/feedback_client'
7
+
8
+ module Racoon
9
+ VERSION = "0.3.2"
10
+ end
@@ -31,7 +31,9 @@ module Racoon
31
31
  end
32
32
 
33
33
  def write(notification)
34
- Config.logger.debug "#{Time.now} [#{host}:#{port}] Device: #{notification.device_token.unpack('H*')} sending #{notification.json_payload}"
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
@@ -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
- if beanstalk('feedback').peek_ready
28
- item = beanstalk('feedback').reserve(1)
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
- begin
38
- if beanstalk('killer').peek_ready
39
- item = beanstalk('killer').reserve(1)
40
- purge_client(item)
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
- rescue Beanstalk::TimedOut
43
- Config.logger.info "(Beanstalkd) Unable to secure a job, operation timed out."
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
- if beanstalk('apns').peek_ready
50
- item = beanstalk('apns').reserve(1)
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
- client.connect! unless client.connected?
89
- client.write(notification)
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
- client.disconnect!
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 client.pem.eql?(certificate)
144
- client.disconnect! if client.connected?
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.1
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-23 00:00:00.000000000 -04:00
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: &70118651237540 !ruby/object:Gem::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: *70118651237540
25
+ version_requirements: *70102541072720
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: beanstalk-client
28
- requirement: &70118651237080 !ruby/object:Gem::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: *70118651237080
36
+ version_requirements: *70102541072160
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: bundler
39
- requirement: &70118651236620 !ruby/object:Gem::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: *70118651236620
47
+ version_requirements: *70102541071700
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: eventmachine
50
- requirement: &70118651236160 !ruby/object:Gem::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: *70118651236160
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