diff --git a/Gemfile.lock b/Gemfile.lock index 6752759..d939bc4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -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 diff --git a/README.textile b/README.textile index b7f5d7f..e7a3900 100644 --- a/README.textile +++ b/README.textile @@ -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. @@ -41,9 +46,12 @@ Then to send a push notification you can either just send a string as the alert 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!') @@ -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]) @@ -70,7 +81,7 @@ You can send other application specific information as well.
   
-    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'})
   
 
diff --git a/apns.gemspec b/apns.gemspec index ec17887..579051c 100644 --- a/apns.gemspec +++ b/apns.gemspec @@ -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 diff --git a/lib/apns/core.rb b/lib/apns/core.rb index c371d4e..17fe692 100644 --- a/lib/apns/core.rb +++ b/lib/apns/core.rb @@ -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) @@ -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 = [] @@ -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 diff --git a/spec/apns/core_spec.rb b/spec/apns/core_spec.rb new file mode 100644 index 0000000..ce469c3 --- /dev/null +++ b/spec/apns/core_spec.rb @@ -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 diff --git a/spec/apns/notification_spec.rb b/spec/apns/notification_spec.rb index b9ab67e..d7c6b90 100644 --- a/spec/apns/notification_spec.rb +++ b/spec/apns/notification_spec.rb @@ -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 @@ -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 diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9edde4a..33d8c2e 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -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'