bpoweski-apnserver 0.0.2 → 0.0.6
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/History.txt +4 -0
- data/Manifest.txt +23 -0
- data/Rakefile +29 -0
- data/VERSION +1 -0
- data/apnserver.gemspec +70 -0
- data/bin/apns +2 -0
- data/bin/apnsend +2 -0
- data/bin/apnserverd +11 -4
- data/lib/apnserver.rb +7 -0
- data/lib/apnserver/client.rb +42 -0
- data/lib/apnserver/notification.rb +63 -0
- data/lib/apnserver/payload.rb +23 -0
- data/lib/apnserver/protocol.rb +21 -0
- data/lib/apnserver/server.rb +34 -0
- data/lib/apnserver/server_connection.rb +9 -0
- metadata +23 -4
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
PostInstall.txt
|
4
|
+
README
|
5
|
+
README.rdoc
|
6
|
+
Rakefile
|
7
|
+
bin/apns
|
8
|
+
bin/apnserverd
|
9
|
+
lib/apnserver.rb
|
10
|
+
lib/apnserver/client.rb
|
11
|
+
lib/apnserver/notification.rb
|
12
|
+
lib/apnserver/payload.rb
|
13
|
+
lib/apnserver/server.rb
|
14
|
+
script/console
|
15
|
+
script/console.cmd
|
16
|
+
script/destroy
|
17
|
+
script/destroy.cmd
|
18
|
+
script/generate
|
19
|
+
script/generate.cmd
|
20
|
+
test/notification_test.rb
|
21
|
+
test/payload_test.rb
|
22
|
+
test/test_apnserver.rb
|
23
|
+
test/test_helper.rb
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'jeweler'
|
5
|
+
Jeweler::Tasks.new do |gemspec|
|
6
|
+
gemspec.name = "apnserver"
|
7
|
+
gemspec.summary = "Apple Push Notification Server"
|
8
|
+
gemspec.description = "A toolkit for proxying and sending Apple Push Notifications"
|
9
|
+
gemspec.email = "bpoweski@3factors.com"
|
10
|
+
gemspec.homepage = "http://github.com/bpoweski/apnserver"
|
11
|
+
gemspec.authors = ["Ben Poweski"]
|
12
|
+
gemspec.add_dependency 'eventmachine'
|
13
|
+
gemspec.add_dependency 'daemons'
|
14
|
+
gemspec.files = FileList['lib/**/*.rb', 'bin/*', '[A-Z]*', 'test/**/*'].to_a
|
15
|
+
end
|
16
|
+
rescue LoadError
|
17
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'rake/testtask'
|
21
|
+
Rake::TestTask.new(:test) do |test|
|
22
|
+
test.test_files = FileList.new('test/**/test_*.rb') do |list|
|
23
|
+
list.exclude 'test/test_helper.rb'
|
24
|
+
end
|
25
|
+
test.libs << 'test'
|
26
|
+
test.verbose = true
|
27
|
+
end
|
28
|
+
|
29
|
+
task :default => [:test]
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.6
|
data/apnserver.gemspec
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{apnserver}
|
8
|
+
s.version = "0.0.6"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Ben Poweski"]
|
12
|
+
s.date = %q{2009-09-14}
|
13
|
+
s.description = %q{A toolkit for proxying and sending Apple Push Notifications}
|
14
|
+
s.email = %q{bpoweski@3factors.com}
|
15
|
+
s.executables = ["apns", "apnsend", "apnserverd"]
|
16
|
+
s.extra_rdoc_files = [
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
"History.txt",
|
21
|
+
"Manifest.txt",
|
22
|
+
"README.rdoc",
|
23
|
+
"Rakefile",
|
24
|
+
"VERSION",
|
25
|
+
"apnserver.gemspec",
|
26
|
+
"bin/apns",
|
27
|
+
"bin/apnsend",
|
28
|
+
"bin/apnserverd",
|
29
|
+
"lib/apnserver.rb",
|
30
|
+
"lib/apnserver/client.rb",
|
31
|
+
"lib/apnserver/notification.rb",
|
32
|
+
"lib/apnserver/payload.rb",
|
33
|
+
"lib/apnserver/protocol.rb",
|
34
|
+
"lib/apnserver/server.rb",
|
35
|
+
"lib/apnserver/server_connection.rb",
|
36
|
+
"test/test_client.rb",
|
37
|
+
"test/test_helper.rb",
|
38
|
+
"test/test_notification.rb",
|
39
|
+
"test/test_payload.rb",
|
40
|
+
"test/test_protocol.rb"
|
41
|
+
]
|
42
|
+
s.homepage = %q{http://github.com/bpoweski/apnserver}
|
43
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
44
|
+
s.require_paths = ["lib"]
|
45
|
+
s.rubygems_version = %q{1.3.5}
|
46
|
+
s.summary = %q{Apple Push Notification Server}
|
47
|
+
s.test_files = [
|
48
|
+
"test/test_client.rb",
|
49
|
+
"test/test_helper.rb",
|
50
|
+
"test/test_notification.rb",
|
51
|
+
"test/test_payload.rb",
|
52
|
+
"test/test_protocol.rb"
|
53
|
+
]
|
54
|
+
|
55
|
+
if s.respond_to? :specification_version then
|
56
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
57
|
+
s.specification_version = 3
|
58
|
+
|
59
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
60
|
+
s.add_runtime_dependency(%q<eventmachine>, [">= 0"])
|
61
|
+
s.add_runtime_dependency(%q<daemons>, [">= 0"])
|
62
|
+
else
|
63
|
+
s.add_dependency(%q<eventmachine>, [">= 0"])
|
64
|
+
s.add_dependency(%q<daemons>, [">= 0"])
|
65
|
+
end
|
66
|
+
else
|
67
|
+
s.add_dependency(%q<eventmachine>, [">= 0"])
|
68
|
+
s.add_dependency(%q<daemons>, [">= 0"])
|
69
|
+
end
|
70
|
+
end
|
data/bin/apns
CHANGED
data/bin/apnsend
CHANGED
data/bin/apnserverd
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
|
2
4
|
require 'getoptlong'
|
3
5
|
require 'rubygems'
|
4
|
-
require 'apnserver'
|
5
6
|
require 'daemons'
|
7
|
+
require 'apnserver'
|
6
8
|
|
7
9
|
def usage
|
8
10
|
puts "Usage: apnserverd [switches] --pem <path>"
|
@@ -47,10 +49,15 @@ end
|
|
47
49
|
|
48
50
|
if pem.nil?
|
49
51
|
usage
|
52
|
+
exit 1
|
50
53
|
else
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
+
Daemonize.daemonize('apnserverd', 'apnserverd')
|
55
|
+
@pid_file = '/var/run/apnserverd.pid'
|
56
|
+
open(@pid_file,"w") {|f| f.write(Process.pid) } # copied from mongrel
|
57
|
+
open(@pid_file,"w") do |f|
|
58
|
+
f.write(Process.pid)
|
59
|
+
File.chmod(0644, @pid_file)
|
60
|
+
end
|
54
61
|
server = ApnServer::Server.new(pem, bind_address, proxy_port)
|
55
62
|
server.start!
|
56
63
|
end
|
data/lib/apnserver.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'socket'
|
3
|
+
|
4
|
+
module ApnServer
|
5
|
+
class Client
|
6
|
+
|
7
|
+
attr_accessor :pem, :host, :port, :password
|
8
|
+
|
9
|
+
def initialize(pem, host = 'gateway.push.apple.com', port = 2195, pass = nil)
|
10
|
+
@pem, @host, @port, @password = pem, host, port, pass
|
11
|
+
end
|
12
|
+
|
13
|
+
def connect!
|
14
|
+
raise "The path to your pem file is not set." unless self.pem
|
15
|
+
raise "The path to your pem file does not exist!" unless File.exist?(self.pem)
|
16
|
+
|
17
|
+
@context = OpenSSL::SSL::SSLContext.new
|
18
|
+
@context.cert = OpenSSL::X509::Certificate.new(File.read(self.pem))
|
19
|
+
@context.key = OpenSSL::PKey::RSA.new(File.read(self.pem), self.password)
|
20
|
+
|
21
|
+
@sock = TCPSocket.new(self.host, self.port)
|
22
|
+
@ssl = OpenSSL::SSL::SSLSocket.new(@sock, @context)
|
23
|
+
@ssl.connect
|
24
|
+
|
25
|
+
return @sock, @ssl
|
26
|
+
end
|
27
|
+
|
28
|
+
def disconnect!
|
29
|
+
@ssl.close
|
30
|
+
@sock.close
|
31
|
+
end
|
32
|
+
|
33
|
+
def write(notification)
|
34
|
+
puts "#{Time.now} [#{host}:#{port}] sending #{notification.alert}"
|
35
|
+
@ssl.write(notification.to_bytes)
|
36
|
+
end
|
37
|
+
|
38
|
+
def connected?
|
39
|
+
@ssl
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'apnserver/payload'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module ApnServer
|
5
|
+
|
6
|
+
class Notification
|
7
|
+
include ApnServer::Payload
|
8
|
+
|
9
|
+
attr_accessor :device_token, :alert, :badge, :sound, :custom
|
10
|
+
|
11
|
+
def payload
|
12
|
+
p = Hash.new
|
13
|
+
[:badge, :alert, :sound, :custom].each do |k|
|
14
|
+
p[k] = send(k) if send(k)
|
15
|
+
end
|
16
|
+
create_payload(p)
|
17
|
+
end
|
18
|
+
|
19
|
+
def json_payload
|
20
|
+
p = JSON.generate(payload)
|
21
|
+
raise PayloadInvalid.new("Payload of #{p.size} is longer than 256") if p.size > 256
|
22
|
+
p
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_bytes
|
26
|
+
json = json_payload
|
27
|
+
[0, 0, device_token.size, device_token, 0, json.size, json].pack("ccca*cca*")
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.valid?(payload)
|
31
|
+
begin
|
32
|
+
Notification.parse(payload)
|
33
|
+
rescue RuntimeError
|
34
|
+
false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.parse(p)
|
39
|
+
buffer = p.dup
|
40
|
+
notification = Notification.new
|
41
|
+
|
42
|
+
header = buffer.slice!(0, 3).unpack('ccc')
|
43
|
+
if header[0] != 0 || header[1] != 0 || header[2] != 32
|
44
|
+
raise RuntimeError.new("Header of notification is invalid: #{header.inspect}")
|
45
|
+
end
|
46
|
+
|
47
|
+
# parse token
|
48
|
+
notification.device_token = buffer.slice!(0, 32).unpack('a*').first
|
49
|
+
|
50
|
+
# parse json payload
|
51
|
+
payload_len = buffer.slice!(0, 2).unpack('cc')
|
52
|
+
result = JSON.parse(buffer.slice!(0, payload_len.last))
|
53
|
+
|
54
|
+
['alert', 'badge', 'sound'].each do |k|
|
55
|
+
notification.send("#{k}=", result['aps'][k]) if result['aps'] && result['aps'][k]
|
56
|
+
end
|
57
|
+
result.delete('aps')
|
58
|
+
notification.custom = result
|
59
|
+
|
60
|
+
notification
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ApnServer
|
2
|
+
|
3
|
+
module Payload
|
4
|
+
|
5
|
+
class PayloadInvalid < RuntimeError
|
6
|
+
end
|
7
|
+
|
8
|
+
def create_payload(payload)
|
9
|
+
case payload
|
10
|
+
when String then { :aps => { :alert => payload } }
|
11
|
+
when Hash then create_payload_from_hash(payload)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_payload_from_hash(payload)
|
16
|
+
custom = payload.delete(:custom)
|
17
|
+
aps = {:aps => payload }
|
18
|
+
aps.merge!(custom) if custom
|
19
|
+
aps
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ApnServer
|
2
|
+
module Protocol
|
3
|
+
|
4
|
+
def post_init
|
5
|
+
@address = Socket.unpack_sockaddr_in(self.get_peername)
|
6
|
+
puts "#{Time.now} [#{address.last}:#{address.first}] CONNECT"
|
7
|
+
end
|
8
|
+
|
9
|
+
def unbind
|
10
|
+
puts "#{Time.now} [#{address.last}:#{address.first}] DISCONNECT"
|
11
|
+
end
|
12
|
+
|
13
|
+
def receive_data(data)
|
14
|
+
(@buf ||= "") << data
|
15
|
+
if notification = ApnServer::Notification.valid?(@buf)
|
16
|
+
queue.push(notification)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module ApnServer
|
2
|
+
|
3
|
+
class Server
|
4
|
+
attr_accessor :client, :bind_address, :port
|
5
|
+
|
6
|
+
def initialize(pem, bind_address = '0.0.0.0', port = 22195)
|
7
|
+
@queue = EM::Queue.new
|
8
|
+
@client = ApnServer::Client.new(pem)
|
9
|
+
@bind_address, @port = bind_address, port
|
10
|
+
end
|
11
|
+
|
12
|
+
def start!
|
13
|
+
EventMachine::run do
|
14
|
+
puts "#{Time.now} Starting APN Server on #{bind_address}:#{port}"
|
15
|
+
|
16
|
+
EM.start_server(bind_address, port, ApnServer::ServerConnection) do |s|
|
17
|
+
s.queue = @queue
|
18
|
+
end
|
19
|
+
|
20
|
+
EventMachine::PeriodicTimer.new(1) do
|
21
|
+
unless @queue.empty?
|
22
|
+
size = @queue.size
|
23
|
+
size.times do
|
24
|
+
@queue.pop do |notification|
|
25
|
+
@client.connect! unless @client.connected?
|
26
|
+
@client.write(notification)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bpoweski-apnserver
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Poweski
|
@@ -43,10 +43,29 @@ extensions: []
|
|
43
43
|
extra_rdoc_files:
|
44
44
|
- README.rdoc
|
45
45
|
files:
|
46
|
+
- History.txt
|
47
|
+
- Manifest.txt
|
46
48
|
- README.rdoc
|
47
|
-
|
49
|
+
- Rakefile
|
50
|
+
- VERSION
|
51
|
+
- apnserver.gemspec
|
52
|
+
- bin/apns
|
53
|
+
- bin/apnsend
|
54
|
+
- bin/apnserverd
|
55
|
+
- lib/apnserver.rb
|
56
|
+
- lib/apnserver/client.rb
|
57
|
+
- lib/apnserver/notification.rb
|
58
|
+
- lib/apnserver/payload.rb
|
59
|
+
- lib/apnserver/protocol.rb
|
60
|
+
- lib/apnserver/server.rb
|
61
|
+
- lib/apnserver/server_connection.rb
|
62
|
+
- test/test_client.rb
|
63
|
+
- test/test_helper.rb
|
64
|
+
- test/test_notification.rb
|
65
|
+
- test/test_payload.rb
|
66
|
+
- test/test_protocol.rb
|
67
|
+
has_rdoc: false
|
48
68
|
homepage: http://github.com/bpoweski/apnserver
|
49
|
-
licenses:
|
50
69
|
post_install_message:
|
51
70
|
rdoc_options:
|
52
71
|
- --charset=UTF-8
|
@@ -67,7 +86,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
67
86
|
requirements: []
|
68
87
|
|
69
88
|
rubyforge_project:
|
70
|
-
rubygems_version: 1.
|
89
|
+
rubygems_version: 1.2.0
|
71
90
|
signing_key:
|
72
91
|
specification_version: 3
|
73
92
|
summary: Apple Push Notification Server
|