racoon 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.mdown +7 -1
- data/bin/racoon-firehose +26 -2
- data/bin/racoon-worker +56 -5
- data/lib/racoon.rb +1 -1
- data/lib/racoon/firehose.rb +19 -36
- data/lib/racoon/worker.rb +17 -55
- metadata +9 -20
data/README.mdown
CHANGED
@@ -8,7 +8,7 @@ has since taken on a different path. How does it differ from apnserver? By a few
|
|
8
8
|
3. Expects certificates as strings instead of paths to files;
|
9
9
|
4. Does not assume there is only one certificate (read: supports multiple projects); and
|
10
10
|
5. Receives packets containing notifications from beanstalkd instead of a listening socket;
|
11
|
-
6. Operates on a distributed architecture (many parallel workers, one firehose.
|
11
|
+
6. Operates on a distributed architecture (many parallel workers, one firehose).
|
12
12
|
|
13
13
|
The above changes were made because of the need to replace an existing APNs provider with something
|
14
14
|
more robust, and better suited to scaling upwards. This APNs provider had a couple requirements:
|
@@ -20,6 +20,12 @@ more robust, and better suited to scaling upwards. This APNs provider had a coup
|
|
20
20
|
It should be noted that the development of this project is independent of the work bpoweski
|
21
21
|
is doing on apnserver. If you're looking for that project, [go here](https://github.com/bpoweski/apnserver).
|
22
22
|
|
23
|
+
## BIT FAT DEPENDENCY WARNING
|
24
|
+
|
25
|
+
Since version 0.6.0, Racoon uses ZMQMachine. However, the mainline ZMQMachine does not support PUSH/PULL
|
26
|
+
sockets as of the time I wrote this (probably still the same way, else I'd have removed this by now).
|
27
|
+
As such, I strongly urge you to [fork this repository](https://github.com/jeremytregunna/zmqmachine) and install it before installing racoon.
|
28
|
+
|
23
29
|
## Description
|
24
30
|
|
25
31
|
Racoon consists of a firehose, which maintains the connections to Apple's APNs service. It also
|
data/bin/racoon-firehose
CHANGED
@@ -6,6 +6,8 @@ require 'rubygems'
|
|
6
6
|
require 'daemons'
|
7
7
|
require 'racoon'
|
8
8
|
require 'csv'
|
9
|
+
require 'yaml'
|
10
|
+
require 'zmqmachine'
|
9
11
|
|
10
12
|
def usage
|
11
13
|
puts "Usage: racoon-firehose [switches]"
|
@@ -54,6 +56,28 @@ Racoon::Config.logger = Logger.new(@log_file)
|
|
54
56
|
if daemon
|
55
57
|
daemonize
|
56
58
|
else
|
57
|
-
puts "Starting racoon
|
59
|
+
puts "Starting racoon firehose."
|
58
60
|
end
|
59
|
-
|
61
|
+
|
62
|
+
ZM::Reactor.new(:firehose).run do |context|
|
63
|
+
firehose = Racoon::Firehose.new(context) do |feedback_record|
|
64
|
+
Racoon::Config.logger.info "[Feedback] Time: #{feedback_record[:feedback_at]} Device: #{feedback_record[:device_token]} Length: #{feedback_record[:length]}"
|
65
|
+
end
|
66
|
+
context.pull_socket(firehose)
|
67
|
+
|
68
|
+
context.periodical_timer(28800) do
|
69
|
+
firehose.connections.each_pair do |key, data|
|
70
|
+
begin
|
71
|
+
project = data[:project]
|
72
|
+
uri = "gateway.#{project[:sandbox] ? 'sandbox.' : ''}push.apple.com"
|
73
|
+
feedback = Racoon::APNS::FeedbackConnection.new(data[:certificate], uri)
|
74
|
+
feedback.connect!
|
75
|
+
feedback.read.each do |record|
|
76
|
+
@feedback_callback.call(record) if @feedback_callback
|
77
|
+
end
|
78
|
+
rescue Errno::EPIPE, OpenSSL::SSL::SSLError, Errno::ECONNRESET
|
79
|
+
feedback.disconnect!
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end.join
|
data/bin/racoon-worker
CHANGED
@@ -6,11 +6,15 @@ require 'rubygems'
|
|
6
6
|
require 'daemons'
|
7
7
|
require 'racoon'
|
8
8
|
require 'csv'
|
9
|
+
require 'yaml'
|
10
|
+
require 'zmqmachine'
|
11
|
+
require 'beanstalk-client'
|
9
12
|
|
10
13
|
def usage
|
11
14
|
puts "Usage: racoon-worker [switches]"
|
12
15
|
puts " --beanstalk <127.0.0.1:11300> csv list of ip:port for beanstalk servers"
|
13
16
|
puts " --tube <racoon> the beanstalk tube to use"
|
17
|
+
puts " --firehose <127.0.0.1:11555> the address of the firehose"
|
14
18
|
puts " --pid </var/run/racoon-worker.pid> the path to store the pid"
|
15
19
|
puts " --log </var/log/racoon-worker.log> the path to store the log"
|
16
20
|
puts " --daemon to daemonize the server"
|
@@ -29,14 +33,16 @@ end
|
|
29
33
|
opts = GetoptLong.new(
|
30
34
|
["--beanstalk", "-b", GetoptLong::REQUIRED_ARGUMENT],
|
31
35
|
["--tube", "-t", GetoptLong::REQUIRED_ARGUMENT],
|
36
|
+
["--firehose", "-f", GetoptLong::REQUIRED_ARGUMENT],
|
32
37
|
["--pid", "-i", GetoptLong::REQUIRED_ARGUMENT],
|
33
38
|
["--log", "-l", GetoptLong::REQUIRED_ARGUMENT],
|
34
39
|
["--help", "-h", GetoptLong::NO_ARGUMENT],
|
35
40
|
["--daemon", "-d", GetoptLong::NO_ARGUMENT]
|
36
41
|
)
|
37
42
|
|
38
|
-
beanstalks = ["127.0.0.1:11300"]
|
39
|
-
|
43
|
+
@beanstalks = ["127.0.0.1:11300"]
|
44
|
+
firehose_address = "127.0.0.1:11555".split(":")
|
45
|
+
@tube = 'racoon'
|
40
46
|
@pid_file = '/var/run/racoon-worker.pid'
|
41
47
|
@log_file = '/var/log/racoon-worker.log'
|
42
48
|
daemon = false
|
@@ -47,9 +53,11 @@ opts.each do |opt, arg|
|
|
47
53
|
usage
|
48
54
|
exit 1
|
49
55
|
when '--beanstalk'
|
50
|
-
beanstalks = CSV.parse(arg)[0]
|
56
|
+
@beanstalks = CSV.parse(arg)[0]
|
51
57
|
when '--tube'
|
52
|
-
tube = arg
|
58
|
+
@tube = arg
|
59
|
+
when '--firehose'
|
60
|
+
firehose_address = arg.split(":")
|
53
61
|
when '--pid'
|
54
62
|
@pid_file = arg
|
55
63
|
when '--log'
|
@@ -61,9 +69,52 @@ end
|
|
61
69
|
|
62
70
|
Racoon::Config.logger = Logger.new(@log_file)
|
63
71
|
|
72
|
+
def beanstalk
|
73
|
+
return @beanstalk if @beanstalk
|
74
|
+
@beanstalk = Beanstalk::Pool.new(@beanstalks)
|
75
|
+
%w{use watch}.each { |s| @beanstalk.send(s, @tube) }
|
76
|
+
@beanstalk.ignore('default')
|
77
|
+
@beanstalk
|
78
|
+
end
|
79
|
+
|
80
|
+
# Expects json ala:
|
81
|
+
# json = {
|
82
|
+
# "project":{
|
83
|
+
# "name":"foobar",
|
84
|
+
# "certificate":"...",
|
85
|
+
# "sandbox":false
|
86
|
+
# },
|
87
|
+
# "bytes":"..."
|
88
|
+
# }
|
89
|
+
def process(job, worker)
|
90
|
+
packet = job.ybody
|
91
|
+
project = packet[:project]
|
92
|
+
|
93
|
+
notification = Racoon::Notification.create_from_packet(packet)
|
94
|
+
|
95
|
+
data = { :project => project, :bytes => notification.to_bytes }
|
96
|
+
worker.send_message(YAML::dump(data))
|
97
|
+
end
|
98
|
+
|
64
99
|
if daemon
|
65
100
|
daemonize
|
66
101
|
else
|
67
102
|
puts "Starting racoon worker."
|
68
103
|
end
|
69
|
-
|
104
|
+
|
105
|
+
ZM::Reactor.new(:worker).run do |context|
|
106
|
+
worker = Racoon::Worker.new(context, ZM::Address.new(firehose_address[0], firehose_address[1], :tcp))
|
107
|
+
context.push_socket(worker)
|
108
|
+
|
109
|
+
context.periodical_timer(0.1) do
|
110
|
+
begin
|
111
|
+
if beanstalk.peek_ready
|
112
|
+
job = beanstalk.reserve(1)
|
113
|
+
process(job, worker)
|
114
|
+
job.delete
|
115
|
+
end
|
116
|
+
rescue Beanstalk::TimedOut
|
117
|
+
Racoon::Config.logger.info "[Beanstalk] Unable to secure job, timed out."
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end.join
|
data/lib/racoon.rb
CHANGED
data/lib/racoon/firehose.rb
CHANGED
@@ -5,59 +5,42 @@
|
|
5
5
|
# connections to Apple, and sending data over the right ones.
|
6
6
|
|
7
7
|
require 'digest/sha1'
|
8
|
-
require '
|
9
|
-
require '
|
8
|
+
require 'zmqmachine'
|
9
|
+
require 'yaml'
|
10
10
|
|
11
11
|
module Racoon
|
12
12
|
class Firehose
|
13
|
-
|
13
|
+
attr_accessor :connections, :feedback_callback
|
14
|
+
|
15
|
+
def initialize(reactor, address = ZM::Address.new('*', 11555, :tcp), &feedback_callback)
|
14
16
|
@connections = {}
|
15
|
-
@
|
16
|
-
@firehose = context.socket(ZMQ::PULL)
|
17
|
+
@reactor = reactor
|
17
18
|
@address = address
|
18
19
|
@feedback_callback = feedback_callback
|
19
20
|
end
|
20
21
|
|
21
|
-
def
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
EventMachine::PeriodicTimer.new(28800) do
|
26
|
-
@connections.each_pair do |key, data|
|
27
|
-
begin
|
28
|
-
uri = "gateway.#{project[:sandbox] ? 'sandbox.' : ''}push.apple.com"
|
29
|
-
feedback = Racoon::APNS::FeedbackConnection.new(data[:certificate], uri)
|
30
|
-
feedback.connect!
|
31
|
-
feedback.read.each do |record|
|
32
|
-
@feedback_callback.call(record) if @feedback_callback
|
33
|
-
end
|
34
|
-
rescue Errno::EPIPE, OpenSSL::SSL::SSLError, Errno::ECONNRESET
|
35
|
-
feedback.disconnect!
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
EventMachine::PeriodicTimer.new(0.1) do
|
41
|
-
received_message = ZMQ::Message.new
|
42
|
-
@firehose.recv(received_message, ZMQ::NOBLOCK)
|
43
|
-
yaml_string = received_message.copy_out_string
|
44
|
-
|
45
|
-
if yaml_string and yaml_string != ""
|
46
|
-
packet = YAML::load(yaml_string)
|
22
|
+
def on_attach(socket)
|
23
|
+
socket.bind(@address)
|
24
|
+
end
|
47
25
|
|
48
|
-
|
49
|
-
|
50
|
-
|
26
|
+
def on_readable(socket, messages)
|
27
|
+
messages.each do |message|
|
28
|
+
packet = YAML::load(message.copy_out_string)
|
29
|
+
apns(packet[:project], packet[:bytes])
|
51
30
|
end
|
52
31
|
end
|
53
32
|
|
33
|
+
private
|
34
|
+
|
54
35
|
def apns(project, bytes, retries=2)
|
55
36
|
uri = "gateway.#{project[:sandbox] ? 'sandbox.' : ''}push.apple.com"
|
56
37
|
hash = Digest::SHA1.hexdigest("#{project[:name]}-#{project[:certificate]}")
|
57
38
|
|
58
39
|
begin
|
59
|
-
connection
|
60
|
-
|
40
|
+
@connections[hash] ||= { :connection => Racoon::APNS::Connection.new(project[:certificate], uri),
|
41
|
+
:certificate => project[:certificate],
|
42
|
+
:sandbox => project[:sandbox] }
|
43
|
+
connection = @connections[hash][:connection]
|
61
44
|
|
62
45
|
connection.connect! unless connection.connected?
|
63
46
|
connection.write(bytes)
|
data/lib/racoon/worker.rb
CHANGED
@@ -4,73 +4,35 @@
|
|
4
4
|
# This module contains the worker which processes notifications before sending them off
|
5
5
|
# down to the firehose.
|
6
6
|
|
7
|
-
require 'beanstalk-client'
|
8
|
-
require 'eventmachine'
|
9
7
|
require 'ffi-rzmq'
|
10
|
-
require '
|
8
|
+
require 'zmqmachine'
|
11
9
|
|
12
10
|
module Racoon
|
13
11
|
class Worker
|
14
|
-
def initialize(
|
15
|
-
@
|
16
|
-
@context = context
|
17
|
-
@firehose = context.socket(ZMQ::PUSH)
|
18
|
-
@tube = tube
|
12
|
+
def initialize(reactor, address)
|
13
|
+
@reactor = reactor
|
19
14
|
@address = address
|
20
|
-
|
21
|
-
@send_batch = true
|
15
|
+
@send_queue = []
|
22
16
|
end
|
23
|
-
|
24
|
-
def start!
|
25
|
-
EventMachine::run do
|
26
|
-
@firehose.connect(@address)
|
27
17
|
|
28
|
-
|
29
|
-
|
30
|
-
@firehose.send_string("")
|
31
|
-
end
|
18
|
+
def on_attach(socket)
|
19
|
+
@socket = socket
|
32
20
|
|
33
|
-
|
34
|
-
begin
|
35
|
-
if beanstalk.peek_ready
|
36
|
-
job = beanstalk.reserve(1)
|
37
|
-
process job
|
38
|
-
job.delete
|
39
|
-
end
|
40
|
-
rescue Beanstalk::TimedOut
|
41
|
-
Config.logger.info "[Beanstalk] Unable to secure job, operation timed out."
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
21
|
+
socket.connect(@address.to_s)
|
45
22
|
end
|
46
23
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
@beanstalk
|
24
|
+
def on_writable(socket)
|
25
|
+
unless @send_queue.empty?
|
26
|
+
message = @send_queue.shift
|
27
|
+
socket.send_message_string(message)
|
28
|
+
else
|
29
|
+
@reactor.deregister_writable(socket)
|
30
|
+
end
|
55
31
|
end
|
56
32
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
# "name":"foobar",
|
61
|
-
# "certificate":"...",
|
62
|
-
# "sandbox":false
|
63
|
-
# },
|
64
|
-
# "bytes":"..."
|
65
|
-
# }
|
66
|
-
def process(job)
|
67
|
-
packet = job.ybody
|
68
|
-
project = packet[:project]
|
69
|
-
|
70
|
-
notification = Notification.create_from_packet(packet)
|
71
|
-
|
72
|
-
data = { :project => project, :bytes => notification.to_bytes }
|
73
|
-
@firehose.send_string(YAML::dump(data))
|
33
|
+
def send_message(message)
|
34
|
+
@send_queue.push(message)
|
35
|
+
@reactor.register_writable(@socket)
|
74
36
|
end
|
75
37
|
end
|
76
38
|
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.6.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: &70102154456440 !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: *70102154456440
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: beanstalk-client
|
28
|
-
requirement: &
|
28
|
+
requirement: &70102154455980 !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: *70102154455980
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
38
|
name: ffi-rzmq
|
39
|
-
requirement: &
|
39
|
+
requirement: &70102154455520 !ruby/object:Gem::Requirement
|
40
40
|
none: false
|
41
41
|
requirements:
|
42
42
|
- - ~>
|
@@ -44,10 +44,10 @@ dependencies:
|
|
44
44
|
version: 0.8.0
|
45
45
|
type: :runtime
|
46
46
|
prerelease: false
|
47
|
-
version_requirements: *
|
47
|
+
version_requirements: *70102154455520
|
48
48
|
- !ruby/object:Gem::Dependency
|
49
49
|
name: bundler
|
50
|
-
requirement: &
|
50
|
+
requirement: &70102154455060 !ruby/object:Gem::Requirement
|
51
51
|
none: false
|
52
52
|
requirements:
|
53
53
|
- - ~>
|
@@ -55,18 +55,7 @@ dependencies:
|
|
55
55
|
version: 1.0.0
|
56
56
|
type: :development
|
57
57
|
prerelease: false
|
58
|
-
version_requirements: *
|
59
|
-
- !ruby/object:Gem::Dependency
|
60
|
-
name: eventmachine
|
61
|
-
requirement: &70355758734300 !ruby/object:Gem::Requirement
|
62
|
-
none: false
|
63
|
-
requirements:
|
64
|
-
- - ! '>='
|
65
|
-
- !ruby/object:Gem::Version
|
66
|
-
version: 0.12.8
|
67
|
-
type: :development
|
68
|
-
prerelease: false
|
69
|
-
version_requirements: *70355758734300
|
58
|
+
version_requirements: *70102154455060
|
70
59
|
description: A distributed Apple Push Notification Service (APNs) provider developed
|
71
60
|
for hosting multiple projects.
|
72
61
|
email: jeremy.tregunna@me.com
|