Skip to content
This repository has been archived by the owner on Jan 29, 2024. It is now read-only.

Support multiple apps #50

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 19 additions & 10 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,28 @@ PATH
GEM
remote: http://rubygems.org/
specs:
diff-lcs (1.1.3)
rspec (2.10.0)
rspec-core (~> 2.10.0)
rspec-expectations (~> 2.10.0)
rspec-mocks (~> 2.10.0)
rspec-core (2.10.1)
rspec-expectations (2.10.0)
diff-lcs (~> 1.1.3)
rspec-mocks (2.10.1)
diff-lcs (1.3)
rspec (3.4.0)
rspec-core (~> 3.4.0)
rspec-expectations (~> 3.4.0)
rspec-mocks (~> 3.4.0)
rspec-core (3.4.3)
rspec-support (~> 3.4.0)
rspec-expectations (3.4.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.4.0)
rspec-mocks (3.4.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.4.0)
rspec-support (3.4.1)

PLATFORMS
ruby

DEPENDENCIES
apns!
rspec
rspec (~> 3.4.0)
rspec-mocks (~> 3.4.0)

BUNDLED WITH
1.13.6
23 changes: 17 additions & 6 deletions README.textile
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,14 @@ After you have your .pem file. Set what host, port, certificate file location on
APNS.host = 'gateway.push.apple.com'
# gateway.sandbox.push.apple.com is default

APNS.pem = '/path/to/pem/file'
# this is the file you just created

APNS.pem = '/path/to/pem/file'
# these the file you just created

# or if you have more than one app
APNS.pems = { app1: '/path/to/pem/file/of/app1', app2: '/path/to/pem/file/of/app2' }
# actually it's identical to
APNS.pems = { default: '/path/to/pem/file' }

APNS.port = 2195
# this is also the default. Shouldn't ever have to set this, but just in case Apple goes crazy, you can.
</code>
Expand All @@ -41,9 +46,12 @@ Then to send a push notification you can either just send a string as the alert
<code>
device_token = '123abc456def'

APNS.send_notification(device_token, 'Hello iPhone!' )
APNS.send_notification(:app1, device_token, 'Hello iPhone!')

APNS.send_notification(device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default')
APNS.send_notification(:app2, device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default')

# if you only have one app and uses APNS.pem = '/path/to/pem/file' to setup the pem, just do
APNS.send_notification(device_token, 'Hello iPhone!')
</code>
</pre>

Expand All @@ -59,6 +67,9 @@ You can also send multiple notifications using the same connection to Apple:

n2 = APNS::Notification.new(device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default')

APNS.send_notifications(:app1, [n1, n2])

# if you only have one app and uses APNS.pem = '/path/to/pem/file' to setup the pem, just do
APNS.send_notifications([n1, n2])
</code>
</pre>
Expand All @@ -70,7 +81,7 @@ You can send other application specific information as well.

<pre>
<code>
APNS.send_notification(device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default',
APNS.send_notification(:app1, device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default',
:other => {:sent => 'with apns gem'})
</code>
</pre>
Expand Down
3 changes: 2 additions & 1 deletion apns.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ DESC
s.rubygems_version = %q{1.3.5}
s.summary = %q{Simple Apple push notification service gem}

s.add_development_dependency 'rspec'
s.add_development_dependency 'rspec', '~>3.4.0'
s.add_development_dependency 'rspec-mocks', '~>3.4.0'

end
86 changes: 62 additions & 24 deletions lib/apns/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,44 @@ module APNS
@host = 'gateway.sandbox.push.apple.com'
@port = 2195
# openssl pkcs12 -in mycert.p12 -out client-cert.pem -nodes -clcerts
@pem = nil # this should be the path of the pem file not the contentes
@pems = {}
# this should be the path of the pem file not the contentes
# Example:
# { some_ios_app : "file/to/pem" }

@pass = nil

class << self
attr_accessor :host, :pem, :port, :pass
attr_accessor :host, :pems, :port, :pass
end

def self.pem=(default_pem_path)
@pems = { default: default_pem_path }
end

def self.send_notification(device_token, message)
def self.send_notification(app_or_device_token, device_token_or_message, message = nil)
app, device_token, message = begin
if message.nil?
[:default, app_or_device_token, device_token_or_message]
else
[app_or_device_token, device_token_or_message, message]
end
end

n = APNS::Notification.new(device_token, message)
self.send_notifications([n])
self.send_notifications(app, [n])
end

def self.send_notifications(notifications)
sock, ssl = self.open_connection
def self.send_notifications(app_or_notifications, notifications = nil)
app, notifications = begin
if notifications.nil?
[:default, app_or_notifications]
else
[app_or_notifications, notifications]
end
end

sock, ssl = self.open_connection(app)

packed_nofications = self.packed_nofications(notifications)

Expand All @@ -45,8 +69,8 @@ def self.packed_nofications(notifications)
bytes
end

def self.feedback
sock, ssl = self.feedback_connection
def self.feedback(app = :default)
sock, ssl = self.feedback_connection(app)

apns_feedback = []

Expand All @@ -63,36 +87,50 @@ def self.feedback

protected

def self.open_connection
raise "The path to your pem file is not set. (APNS.pem = /path/to/cert.pem)" unless self.pem
raise "The path to your pem file does not exist!" unless File.exist?(self.pem)
def self.open_connection(app = :default)
pem = self.pems[app]
raise "The path to your pem file is not set. (APNS.pem = /path/to/cert.pem)" unless pem
raise "The path to your pem file does not exist!" unless File.exist?(pem)

context = OpenSSL::SSL::SSLContext.new
context.cert = OpenSSL::X509::Certificate.new(File.read(self.pem))
context.key = OpenSSL::PKey::RSA.new(File.read(self.pem), self.pass)
context = build_context(pem)

sock = TCPSocket.new(self.host, self.port)
ssl = OpenSSL::SSL::SSLSocket.new(sock,context)
sock = build_socket(self.host, self.port)
ssl = build_ssl(context, sock)
ssl.connect

return sock, ssl
end

def self.feedback_connection
raise "The path to your pem file is not set. (APNS.pem = /path/to/cert.pem)" unless self.pem
raise "The path to your pem file does not exist!" unless File.exist?(self.pem)
def self.feedback_connection(app = :default)
pem = self.pems[app]
raise "The path to your pem file is not set. (APNS.pem = /path/to/cert.pem)" unless pem
raise "The path to your pem file does not exist!" unless File.exist?(pem)

context = OpenSSL::SSL::SSLContext.new
context.cert = OpenSSL::X509::Certificate.new(File.read(self.pem))
context.key = OpenSSL::PKey::RSA.new(File.read(self.pem), self.pass)
context = build_context(pem)

fhost = self.host.gsub('gateway','feedback')
puts fhost

sock = TCPSocket.new(fhost, 2196)
ssl = OpenSSL::SSL::SSLSocket.new(sock,context)
sock = build_socket(fhost, 2196)
ssl = build_ssl(context, sock)
ssl.connect

return sock, ssl
end

def self.build_context(pem)
context = OpenSSL::SSL::SSLContext.new
pem_content = File.read(pem)
context.cert = OpenSSL::X509::Certificate.new(pem_content)
context.key = OpenSSL::PKey::RSA.new(pem_content, self.pass)
context
end

def self.build_socket(host, port)
TCPSocket.new(host, port)
end

def self.build_ssl(context, socket)
OpenSSL::SSL::SSLSocket.new(socket,context)
end
end
130 changes: 130 additions & 0 deletions spec/apns/core_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
require File.dirname(__FILE__) + '/../spec_helper'

describe APNS do
before do
::APNS.host = "gateway.abc.com"
::APNS.port = 1234
allow(File).to receive(:exist?) { true }
end

describe ".send_notifications" do
it "accepts default app and pem" do
::APNS.pem = 'default_pem'

n1 = APNS::Notification.new("token1", "message1")
n2 = APNS::Notification.new("token1", "message2")

context = OpenStruct.new
socket = OpenStruct.new
ssl = OpenStruct.new

expect(socket).to receive(:close)
expect(ssl).to receive(:close)
expect(ssl).to receive(:write).twice

expect(APNS).to receive(:build_context).with('default_pem') { context }
expect(APNS).to receive(:build_socket).with(::APNS.host, ::APNS.port) { socket }
expect(APNS).to receive(:build_ssl).with(context, socket) { ssl }

::APNS.send_notifications([n1, n2])
end


it "accepts an app key" do
::APNS.pems = { app1: 'cert1'}

n1 = APNS::Notification.new("token1", "message1")
n2 = APNS::Notification.new("token1", "message2")

context = OpenStruct.new
socket = OpenStruct.new
ssl = OpenStruct.new

expect(socket).to receive(:close)
expect(ssl).to receive(:close)
expect(ssl).to receive(:write).twice

expect(APNS).to receive(:build_context).with('cert1') { context }
expect(APNS).to receive(:build_socket).with(::APNS.host, ::APNS.port) { socket }
expect(APNS).to receive(:build_ssl).with(context, socket) { ssl }

::APNS.send_notifications(:app1, [n1, n2])
end
end

describe ".send_notification" do
it "accepts default app and pem" do
::APNS.pem = 'default_pem'

context = OpenStruct.new
socket = OpenStruct.new
ssl = OpenStruct.new

expect(socket).to receive(:close)
expect(ssl).to receive(:close)
expect(ssl).to receive(:write)

expect(APNS).to receive(:build_context).with('default_pem') { context }
expect(APNS).to receive(:build_socket).with(::APNS.host, ::APNS.port) { socket }
expect(APNS).to receive(:build_ssl).with(context, socket) { ssl }

::APNS.send_notification("device_token1", message: "message", otherstuff: "others")
end

it "accepts an app key" do
::APNS.pems = { app1: 'cert1'}

context = OpenStruct.new
socket = OpenStruct.new
ssl = OpenStruct.new

expect(socket).to receive(:close)
expect(ssl).to receive(:close)
expect(ssl).to receive(:write)

expect(APNS).to receive(:build_context).with('cert1') { context }
expect(APNS).to receive(:build_socket).with(::APNS.host, ::APNS.port) { socket }
expect(APNS).to receive(:build_ssl).with(context, socket) { ssl }

::APNS.send_notification(:app1, "device_token1", "message1")
end
end

describe ".feedback" do
it "accept default app and pem" do
::APNS.pem = 'default_pem'

context = OpenStruct.new
socket = OpenStruct.new
ssl = OpenStruct.new

expect(socket).to receive(:close)
expect(ssl).to receive(:read).with(38)
expect(ssl).to receive(:close)

expect(APNS).to receive(:build_context).with('default_pem') { context }
expect(APNS).to receive(:build_socket).with("feedback.abc.com", 2196) { socket }
expect(APNS).to receive(:build_ssl).with(context, socket) { ssl }

::APNS.feedback
end

it "accepts an app key" do
::APNS.pems = { app1: 'cert1'}

context = OpenStruct.new
socket = OpenStruct.new
ssl = OpenStruct.new

expect(socket).to receive(:close)
expect(ssl).to receive(:read).with(38)
expect(ssl).to receive(:close)

expect(APNS).to receive(:build_context).with('cert1') { context }
expect(APNS).to receive(:build_socket).with("feedback.abc.com", 2196) { socket }
expect(APNS).to receive(:build_ssl).with(context, socket) { ssl }

::APNS.feedback(:app1)
end
end
end
4 changes: 2 additions & 2 deletions spec/apns/notification_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

it "should have a priority if content_availible is set" do
n = APNS::Notification.new('device_token', {:content_available => true})
n.content_available.should be_true
n.content_available.should be_truthy
n.priority.should eql(5)
end

Expand Down Expand Up @@ -48,7 +48,7 @@
describe '#packaged_notification' do
it "should package the token" do
n = APNS::Notification.new('device_token', {:alert => 'Hello iPhone', :badge => 3, :sound => 'awesome.caf'})
n.stub!(:message_identifier).and_return('aaaa') # make sure the message_identifier is not random
allow(n).to receive(:message_identifier).and_return('aaaa') # make sure the message_identifier is not random
Base64.encode64(n.packaged_notification).should == "AQAG3vLO/YTnAgBAeyJhcHMiOnsiYWxlcnQiOiJIZWxsbyBpUGhvbmUiLCJi\nYWRnZSI6Mywic291bmQiOiJhd2Vzb21lLmNhZiJ9fQMABGFhYWEEAAQAAAAA\nBQABCg==\n"
end
end
Expand Down
1 change: 1 addition & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require 'rubygems'
gem 'rspec', '>= 1.2.8'
require 'rspec'
require 'rspec/mocks'
require File.join(File.dirname(__FILE__), '..', 'lib', 'apns')
require 'base64'