Giter Site home page Giter Site logo

apn_sender's Introduction

Code Climate Build Status

Synopsis

Need to send background notifications to an iPhone application over a persistent connection in Ruby? Keep reading...

The Story

So you're building the server component of an iPhone application in Ruby and you want to send background notifications through the Apple Push Notification servers. This doesn't seem too bad at first, but then you read in the Apple Documentation that Apple's servers may treat non-persistent connections as a Denial of Service attack. Since Rails has no easy way to maintain a persistent connection internally, things start to look complicated.

This gem includes a background daemon which processes background messages from your application and sends them along to Apple over a single, persistent socket. It also includes the ability to query the Feedback service, helper methods for enqueueing your jobs, and a sample monit config to make sure the background worker is around when you need it.

Yet another ApplePushNotification interface?

Yup. There's some great code out there already, but we didn't like the idea of getting banned from the APN gateway for establishing a new connection each time we needed to send a batch of messages, and none of the libraries I found handled maintaining a persistent connection.

Current Status

This gem has been used in production, on 500px, sending hundreds of millions, if not, billions of notifications.

Usage

APN sender can use Resque or Sidekiq to send asynchronous messages, if none of them are installed it creates a new thread to send messages.

1. Use a background processor or not.

You can either use Resque or Sidekiq, I strongly advice using Sidekiq, as apn_sender uses a connection pool for the apple socks.

2. Queueing Messages From Your Application

To queue a message for sending through Apple's Push Notification service from your Rails application:

APN.notify_async(token, opts_hash)

Where token is the unique identifier of the iPhone to receive the notification and opts_hash can have any of the following keys:

  • :alert ## The alert to send
  • :badge ## The badge number to send
  • :sound ## The sound file to play on receipt, or true to play the default sound installed with your app
  • :content_available ## Lets the remote notification act as a "silent" notification.

If any other keys are present they'll be be passed along as custom data to your application.

3. Sending Queued Messages

Put your apn_development.pem and apn_production.pem certificates from Apple in your RAILS_ROOT/config/certs directory.

You also can configure some extra settings:

APN.root = 'RAILS_ROOT/config/certs' # root to certificates folder
APN.certificate_name = 'apn_production.pem' # certificate filename
APN.host = 'apple host (on development sandbox url is used by default)'
APN.password = 'certificate_password'
APN.pool_size = 1 # number of connections on the pool
APN.pool_timeout = 5 # timeout in seconds for connection pool
APN.logger = Logger.new(File.join(Rails.root, 'log', 'apn_sender.log'))
APN.truncate_alert = true # enable the truncation of the alert for notifications that exceed the max allowed bytes
APN.backend = :sidekiq # use sidekiq backend

Check logs/apn_sender.log for debugging output. In addition to logging any major errors there, apn_sender hooks into the Resque::Worker logging to display any verbose or very_verbose worker output in apn_sender.log file as well. On latest versions apn_sender will use Rails.logger as the default logger.

4. Checking Apple's Feedback Service

Since push notifications are a fire-and-forget sorta deal, where you get no indication if your message was received (or if the specified recipient even exists), Apple needed to come up with some other way to ensure their network isn't clogged with thousands of bogus messages (e.g. from developers sending messages to phones where their application used to be installed, but where the user has since removed it). Hence, the Feedback Service.

It's actually really simple - you connect to them periodically and they give you a big dump of tokens you shouldn't send to anymore. The gem wraps this up nicely -- just call:

 # APN::Feedback accepts the same optional :environment
 # and :cert_path / :full_cert_path options as APN::Sender
 feedback = APN::Feedback.new()

 tokens = feedback.tokens # Array of device tokens
 tokens.each do |token|
   # ... custom logic here to stop you app from
   # sending further notifications to this token
 end

If you're interested in knowing exactly when Apple determined each token was expired (which can be useful in determining if the application re-registered with your service since it first appeared in the expired queue):

 items = feedback.data # Array of APN::FeedbackItem elements
 items.each do |item|
   item.token
   item.timestamp
   # ... custom logic here
 end

The Feedback Service works as a big queue. When you connect it pops off all its data and sends it over the wire at once, which means connecting a second time will return an empty array, so for ease of use a call to either +tokens+ or +data+ will connect once and cache the data. If you call either one again it'll continue to use its cached version (rather than connecting to Apple a second time to retrieve an empty array, which is probably not what you want).

Forcing a reconnect is as easy as calling either method with the single parameter +true+, but be sure you've already used the existing data because you'll never get it back.

Warning: No really, check Apple's Feedback Service occasionally

If you're sending notifications, you should definitely call one of the receive methods periodically, as Apple's policies require it and they apparently monitor providers for compliance. I'd definitely recommend throwing together a quick rake task to take care of this for you (the whenever library provides a nice wrapper around scheduling tasks to run at certain times (for systems with cron enabled)).

Just for the record, this is essentially what you want to have whenever run periodically for you:

def self.clear_uninstalled_applications
  feedback_data = APN::Feedback.new(:environment #> :production).data

  feedback_data.each do |item|
    user = User.find_by_iphone_token( item.token )

    if user.iphone_token_updated_at && user.iphone_token_updated_at > item.timestamp
      return true # App has been reregistered since Apple determined it'd been uninstalled
    else
      user.update_attributes(iphone_token: nil, iphone_token_updated_at: Time.now)
    end
  end
end

Keeping Your Workers Working

There's also an included sample apn_sender.monitrc file in the contrib/ folder to help monit handle server restarts and unexpected disasters.

Installation

Add this line to your application's Gemfile:

gem 'apn_sender', require: 'apn'

And then execute:

$ bundle

Or install it yourself as:

$ gem install apn_sender

To add a few useful rake tasks for running workers, add the following line to your Rakefile:

  require 'apn/tasks'

License

APN Sender is released under the MIT License.

apn_sender's People

Contributors

althafh avatar amitsaxena avatar arthurnn avatar atomkirk avatar bitdeli-chef avatar botvinik avatar caueguerra avatar colszowka avatar dixia avatar freeatnet avatar gamafranco avatar hackedunit avatar hiroprot avatar huo-ju avatar ivanushakov avatar jayztemplier avatar jmif avatar kdonovan avatar lukeasrodgers avatar mattsprig avatar nolamesa avatar robinbrandt avatar robj avatar simonmellor avatar sukeerthiadiga avatar zachaysan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

apn_sender's Issues

mdm support

Hi,
Does you gem support mdm for ios? If yes, can you please explain how to use it? If not, can you please add it?

Thanks.

Unable to connect to sandbox

when i execute
feedback= APN::Feedback.new(:environment => :development)

i get
=> #<APN::Feedback: Connection not currently established to feedback.sandbox.push.apple.com on 2196>

i alse have rake taks running
rake apn:sender RAILS_ENV=development

Issue with resque > 1.23.0

Launching rake apn:sender with resque 1.24 gives this error :

/Users/Wizzard/.rvm/gems/ruby-1.9.3-p429/gems/apn_sender-1.0.5/lib/resque/hooks/before_unregister_worker.rb:10:in unregister_worker' /Users/Wizzard/.rvm/gems/ruby-1.9.3-p429/gems/resque-1.24.1/lib/resque/worker.rb:176:inrescue in work'
/Users/Wizzard/.rvm/gems/ruby-1.9.3-p429/gems/resque-1.24.1/lib/resque/worker.rb:130:in work' /Users/Wizzard/.rvm/gems/ruby-1.9.3-p429/gems/apn_sender-1.0.5/lib/apn/tasks.rb:24:inblock (2 levels) in <top (required)>'
/Users/Wizzard/.rvm/gems/ruby-1.9.3-p429/bin/ruby_noexec_wrapper:14:in eval' /Users/Wizzard/.rvm/gems/ruby-1.9.3-p429/bin/ruby_noexec_wrapper:14:in

'

send push to multiple tokens

Hi, guys, so I have question. What is the rigth way to send push notifications to array of tokens ?
Example, input array ["token1", "token2", "token3"]
What I do

["token1", "token2", "token3"].each do |token|
   APN.notify_async(token, options blah blah)
end

I think this is not a right way to send multiple notifications. Because each time created new persistent connection to apn socket. Or connection always opened ? Please correct me.

SSL Send issues with Sidekiq

Push notifications work fine, until there is some period of inactivity.

After that, the job raises the following errors:

An Errno::EPIPE occurred in background at 2013-10-13 15:39:30 UTC :

Broken pipe
/usr/lib/ruby/1.9.1/openssl/buffering.rb:317:in `syswrite'

It seems the pool timed out, but the job doesn't try to reconnect.

Can't configure RedisToGo url for rake apn:sender

I cannot get apn_sender to use my RedisToGo url, it uses the default localhost regardless of me setting this in a resque.rb initializer:

if ENV["REDISTOGO_URL"] && uri = URI.parse(ENV["REDISTOGO_URL"])
  Resque.redis = Redis.new(host: uri.host, port: uri.port, password: uri.password)
end

I have this in my Procfile on Heroku:

apn: bundle exec rake apn:sender
*** Starting worker to send apple notifications in the background from Fredriks-iMac.local:53010:apple_push_notifications

Update README

We need to add the new features that are on version 2.0:

  • sidekiq
  • multithread
  • connection pooling

Feedback Service Returns Invalid Tokens And Timestamps

I'm getting strange results from the feedback service. Most of the tokens are 140 chars long instead of 64. The timestamp is totally off sometime, like some date in 1989. Sometimes the Token is just "3b" and the timestamp: 2066-01-15T05:27:03-08:00

I'm just doing this:

feedback_data = APN::Feedback.new(:environment => :production).data
feedback_data.each do |item|

accessing item.token and item.timestamp here

end

Any idea what's the problem here? Thanks in advance!

Need multiple apn_sender daemons

Say I have 1 server running several rails apps.

Each rails app has it's own Apple Push Notification messages, each bound for a different application, thus different PEM certificates.

It seems that apn_sender doesn't start multiple times, nor can I specify during APN.notify which PEM certificate to use.

I'm more than willing to help mod, but I could use some guidance as to how you'd like me to do it (before I just slam a bunch of keys into your redis namespace)

Where do I put the APN config for resque workers?

First of all, sorry if this is a noob question, but I've spent a lot of time with this without success. I set up this with resque, and all goes right if I follow your default configuration. But when I'm trying to change the APN.password it throws an error. The same happens if I try to change the cert_name.

I put on config/environments/production.rb but I think it's not the right place since who send those notifications are the resque workers.

Thank you in advance and thank you for this great gem!

Sidekiq async notifications fail all the time

I see that the majority of the messages are failing with an error:

OpenSSL::SSL::SSLError: SSL_write: bad write retry

screen shot 2014-01-27 at 11 18 36 am

I see the logs and I see that the apn_sender log stops like 3 days ago which afaik means that the worker died? Have you seen that behavior again? Can you think of any workaround?

I am in version 2.0.0

Enhanced Notification Format not supported (incl 2048 byte limit)

Hi there,

Apple introduced a new byte format of messages that supports up to 2048 byte limit for messages etc. and a lot more. Be careful, the byte format of these new messages is different. So just sending more bytes via old commands does not work, regardless whether the apn sender limitation within this code has been increased.

Compare this here Figure 5-1 from here (the new one - command level 2):
https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/CommunicatingWIthAPS.html#//apple_ref/doc/uid/TP40008194-CH101-SW4

to here Figure A -1 (very old - command level 0):
https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/LegacyFormat.html#//apple_ref/doc/uid/TP40008194-CH105-SW2

and here Figure A - 2 (newer - command level 1)
https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/LegacyFormat.html#//apple_ref/doc/uid/TP40008194-CH105-SW5

The last two are (now) flagged as legacy and anything send via this format gets treated accordingly (incl limitations)
Only the newest one supports the larger messages and these other stuff.

Let's see whether we find the time to open a pull request :)
Cheers
SH

APN feedback not connecting

Hi,

I try to use the APN feedback to get the invalid tokens, so that we can remove them from the database.

But this is what I get on the console in production:

irb(main):011:0>  feedback = APN::Feedback.new()
=> #
irb(main):004:0>  tokens = feedback.tokens
=> []

The server sends push with notify_sync and notify_async. So that is working.
There are invalid tokens on my database, so the array should not be empty.

Thanks.

Special Characters preventing notification from being delivered

We have noticed that when certain special characters are used, specifically apostrophe (’) and ellipsis (…), the notification does not get delivered to the device. The notifications show in the redis log below:

https://gist.github.com/875746

You can see that the elipses goes in as \u2026 and comes back out as \xe2\x80\xa6. I'm not sure if this is a Redis issue or something that can be done from the apn_sender side.

Have you heard or experienced anything like this or have a hint as to where I could look in the source to test with some of these characters?

uninitialized constant APN::Jobs::SidekiqNotificationJob

On some notifications, I'm getting this error, on 2.0.1

A NameError occurred in background at 2014-01-10 19:06:41 UTC : 

uninitialized constant APN::Jobs::SidekiqNotificationJob 
/home/imaginary/lenshare2.imaginarycloud.com/shared/bundle/ruby/1.9.1/gems/activesupport-3.2.14/lib/active_support/inflector/methods.rb:230:in `block in constantize' 

------------------------------- 
Backtrace: 
------------------------------- 

/home/imaginary/lenshare2.imaginarycloud.com/shared/bundle/ruby/1.9.1/gems/activesupport-3.2.14/lib/active_support/inflector/methods.rb:230:in `block in constantize' 
/home/imaginary/lenshare2.imaginarycloud.com/shared/bundle/ruby/1.9.1/gems/activesupport-3.2.14/lib/active_support/inflector/methods.rb:229:in `each' 
/home/imaginary/lenshare2.imaginarycloud.com/shared/bundle/ruby/1.9.1/gems/activesupport-3.2.14/lib/active_support/inflector/methods.rb:229:in `constantize' 
/home/imaginary/lenshare2.imaginarycloud.com/shared/bundle/ruby/1.9.1/gems/activesupport-3.2.14/lib/active_support/core_ext/string/inflections.rb:54:in `constantize' 
/home/imaginary/lenshare2.imaginarycloud.com/shared/bundle/ruby/1.9.1/gems/sidekiq-2.17.1/lib/sidekiq/processor.rb:43:in `block in process' 
/home/imaginary/lenshare2.imaginarycloud.com/shared/bundle/ruby/1.9.1/gems/celluloid-0.15.2/lib/celluloid/calls.rb:25:in `call' 
/home/imaginary/lenshare2.imaginarycloud.com/shared/bundle/ruby/1.9.1/gems/celluloid-0.15.2/lib/celluloid/calls.rb:25:in `public_send' 
/home/imaginary/lenshare2.imaginarycloud.com/shared/bundle/ruby/1.9.1/gems/celluloid-0.15.2/lib/celluloid/calls.rb:25:in `dispatch' 
/home/imaginary/lenshare2.imaginarycloud.com/shared/bundle/ruby/1.9.1/gems/celluloid-0.15.2/lib/celluloid/calls.rb:67:in `dispatch' 
/home/imaginary/lenshare2.imaginarycloud.com/shared/bundle/ruby/1.9.1/gems/celluloid-0.15.2/lib/celluloid/future.rb:14:in `block in new' 
/home/imaginary/lenshare2.imaginarycloud.com/shared/bundle/ruby/1.9.1/gems/celluloid-0.15.2/lib/celluloid/thread_handle.rb:13:in `block in initialize' 
/home/imaginary/lenshare2.imaginarycloud.com/shared/bundle/ruby/1.9.1/gems/celluloid-0.15.2/lib/celluloid/internal_pool.rb:100:in `call' 
/home/imaginary/lenshare2.imaginarycloud.com/shared/bundle/ruby/1.9.1/gems/celluloid-0.15.2/lib/celluloid/internal_pool.rb:100:in `block in create' 

------------------------------- 
Data: 
------------------------------- 

* data: {:message=> 
{"retry"=>true, 
"queue"=>"default", 
"class"=>"APN::Jobs::SidekiqNotificationJob", 
"args"=> 
["343d6a73d8e39bc988a995215a72d0f8c3d78754a4c1c3a8c477ded774336ccf", 
{"alert"=>"Miguel commented an instant 18:17: Fab", 
"sound"=>true, 
"badge"=>5}], 
"jid"=>"71ded2ece0f7f9917f4dba2b", 
"enqueued_at"=>1389380801.0102606}} 

Not sending notification from Heroku

Hi Kali,

I'm testin apn_sender with resque, in my sandbox work fine, the notifications are sent and received in my iphone, but in Heroku the notifications not arrive to my iphone

The resque jobs are working, in the log I see when the #perform method when proper values are executed, no errors, but the notificactions don't arrive to my iphone

Any clue what I'm doing wrong?

Persistent Sockets

How does this send notifications through the same persistent socket? If I queue notifications and run a worker in dev then step through it in the debugger, setup_connection is always called when the socket is written to. The @socket ivar is always nil, I assume because jobs are forked before they are run so how is the socket method in APN::Connection:Base supposed to evaluate?

      def socket
        setup_connection unless @socket
        return @socket
      end

PS. Great library!

Version bump

Any plans for a version bump? Need help with anything to get there?

Daemon not running

When I try to start the daemon on my server it will tell me:
apn_sender: process with pid 1451 started.

But it is actually not running; "apn_sender status" tells me that there are "no instances running" and there also isn't a .pid file in ../tmp/pids

When I start resque-web I can see that the notifications are still queued and haven't been sent.

I can send the notifications manually by using "rake apn:sender", however "rake apn:senders" doesn't work.

Configuration: Rails 3.0.9, Passenger + nginx, deployed using Capistrano via git

The daemon works fine when I run the app locally on OS X.

Any help?

Resque worker processed the job but push notification is not sent

Resque.enqueue(APN::Jobs::ResqueNotificationJob, token, opts) is being called and a job is being scheduled whenever i execute a code APN.notify_async(token, opts) from my rails application in development environment. This job is successfully getting processed with no failure as shown at url /resque/stats/resque
But the notification is not being pushed and no output is logged at 'logs/apn_sender.log'.

Then i tried to send push notification directly from rails console by calling: APN::Jobs::ResqueNotificationJob.perform(token, opts)
This do send push notification.

herkou run console can't create /app/log/apn_sender.log

I'm using rails and heroku. It works perfectly as apn sender. But I got an error in trying to use "heroku run console".

/app/vendor/ruby-2.0.0/lib/ruby/2.0.0/open-uri.rb:36:in `initialize': No such file or directory - /app/log/apn_sender.log (Errno::ENOENT)
        from /app/vendor/ruby-2.0.0/lib/ruby/2.0.0/open-uri.rb:36:in `open'
        from /app/vendor/ruby-2.0.0/lib/ruby/2.0.0/open-uri.rb:36:in `open'
        from /app/vendor/ruby-2.0.0/lib/ruby/2.0.0/logger.rb:601:in `create_logfile'
        from /app/vendor/ruby-2.0.0/lib/ruby/2.0.0/logger.rb:596:in `open_logfile'
        from /app/vendor/ruby-2.0.0/lib/ruby/2.0.0/logger.rb:551:in `initialize'
        from /app/vendor/ruby-2.0.0/lib/ruby/2.0.0/logger.rb:318:in `new'
        from /app/vendor/ruby-2.0.0/lib/ruby/2.0.0/logger.rb:318:in `initialize'
        from /app/vendor/bundle/ruby/2.0.0/gems/activesupport-3.2.16/lib/active_support/core_ext/logger.rb:72:in `initialize'
        from /app/vendor/bundle/ruby/2.0.0/gems/apn_sender-2.0.1/lib/apn/railtie.rb:11:in `new'
        from /app/vendor/bundle/ruby/2.0.0/gems/apn_sender-2.0.1/lib/apn/railtie.rb:11:in `block in '
        from /app/vendor/bundle/ruby/2.0.0/gems/railties-3.2.16/lib/rails/initializable.rb:30:in `instance_exec'
        from /app/vendor/bundle/ruby/2.0.0/gems/railties-3.2.16/lib/rails/initializable.rb:30:in `run'
        from /app/vendor/bundle/ruby/2.0.0/gems/railties-3.2.16/lib/rails/initializable.rb:55:in `block in run_initializers'
        from /app/vendor/bundle/ruby/2.0.0/gems/railties-3.2.16/lib/rails/initializable.rb:54:in `each'
        from /app/vendor/bundle/ruby/2.0.0/gems/railties-3.2.16/lib/rails/initializable.rb:54:in `run_initializers'
        from /app/vendor/bundle/ruby/2.0.0/gems/railties-3.2.16/lib/rails/application.rb:136:in `initialize!'
        from /app/vendor/bundle/ruby/2.0.0/gems/railties-3.2.16/lib/rails/railtie/configurable.rb:30:in `method_missing'
        from /app/config/environment.rb:10:in `'
        from /app/vendor/bundle/ruby/2.0.0/gems/activesupport-3.2.16/lib/active_support/dependencies.rb:251:in `require'
        from /app/vendor/bundle/ruby/2.0.0/gems/activesupport-3.2.16/lib/active_support/dependencies.rb:251:in `block in require'
        from /app/vendor/bundle/ruby/2.0.0/gems/activesupport-3.2.16/lib/active_support/dependencies.rb:236:in `load_dependency'
        from /app/vendor/bundle/ruby/2.0.0/gems/activesupport-3.2.16/lib/active_support/dependencies.rb:251:in `require'
        from /app/vendor/bundle/ruby/2.0.0/gems/railties-3.2.16/lib/rails/application.rb:103:in `require_environment!'
        from /app/vendor/bundle/ruby/2.0.0/gems/railties-3.2.16/lib/rails/commands.rb:40:in `'
        from script/rails:6:in `require'
        from script/rails:6:in `'

here is my .gitignore and .slugignore

> cat .gitignore
.bundle
db/*.sqlite3
log/*.log
tmp/
.sass-cache/
coverage/
.envrc
tags
.project
*~

> cat .slugignore
coverage/
doc/
*.log
spec/
test/
README.rst

Is there no way to use "heroku run console" with apn_sender?

how to skip the pass phrase ?

I generated the key with pass phrase. now I got the pem file. and the notification can be sent.

now my issue is, it asked me for

Enter PEM pass phrase:  

when I send notification or get feedback. I have the pass phrase, how to set the pass phrase as default? so no need to type it each time? thanks for your help.

Error: undefined method 'notify' for :sidekiq:Symbol

Hello guys, i'm having a bit of trouble in sending those push notifications. When "notify_async(ids,opts)" is called i get the following error:

NoMethodError (undefined method `notify' for :sidekiq:Symbol):

Here's the screenshot of the code line

def self.send_apple_push_notification(projeto, ids, message)
 projeto.configure_apn(projeto)
 options = { alert: message, badge: "+1", sound: true, other_information: ''}
 ids.each do |id|
   APN.notify_async(id, options)
 end
end

I've chosen sidekiq as my backend just like the README suggests

APN.backend = :sidekiq # use sidekiq backend

Here's the rest of the configuration. This was done with the intent to make the project work with multiple certificates.

def configure_apn(projeto)
  path = nil
  if projeto.desenvolvimento
    path = projeto.apn_development.path
  else
    path = projeto.apn_production.path
  end

  unless path.nil?
    last_slash = path.rindex("/")
    folder = path[0..last_slash]
    file = path[last_slash+1..path.length]

    APN.root = folder # root to certificates folder
    APN.certificate_name = file # certificate filename
    if projeto.desenvolvimento
      APN.host = projeto.apn_host
      APN.password = projeto.apn_password_development
    else
      APN.password = projeto.apn_password_production
    end
    APN.pool_size = 1 # number of connections on the pool
    APN.pool_timeout = 5 # timeout in seconds for connection pool
    APN.logger = Logger.new(File.join(Rails.root, 'log', 'apn_sender.log'))
    APN.truncate_alert = true 
    APN.backend = :sidekiq # use sidekiq backend
  end
end

Broken connections are not repaired

Steps to reproduce:

  1. Launch APN sender in a loop where the connection pool is persisted despite exceptions (i.e., Sidekiq).
  2. Start sending notifications.
  3. Simulate network problems.

Expected behaviour: Broken connections are repaired or discarded, failed messages are possibly retried.
Observed behaviour: Broken connections are supplied for the next messages.

No umlauts in APN message?

i try to send words with umlauts (ä,ö,ü) but i get an rails error while sending:

.../push_it/app/controllers/push_controller.rb:4: invalid multibyte char (US-ASCII)
.../push_it/app/controllers/push_controller.rb:4: invalid multibyte char (US-ASCII)
.../push_it/app/controllers/push_controller.rb:4: syntax error, unexpected $end, expecting ')'
...ch unser Weihachtsspecial veröffentlicht. Schaut es euch an...           

Messages not being sent

Hi,

Messages are sent on alternations. I'm using sidekiq and this is what happens with notify async:

Message 1 - worker connects to APN - message arrives to the iphone
Message 2 - worker is connect, apn log says it was sent, never arrives
Message 3 - worker reconnects to APN - message arrives to the iphone
Message 4 - worker is connect, apn log says it was sent, never arrives
Message 5 - worker reconnects to APN - message arrives to the iphone

and so on.

Sidekiq Password not set using development mode

Just trying to use apn_sender with sidekiq in development mode:

APN.host = 'gateway.sandbox.push.apple.com'
APN.password = 'justapw'
APN.pool_size = 1 # number of connections on the pool
APN.pool_timeout = 5 # timeout in seconds for connection pool
device_token = "123"
APN.backend = :sidekiq
APN.notify_async(device_token, :alert => 'PushPlugin works!!', :badge => 1, :sound => 'beep.wav')

And sidekiq askes me for a password?

2014-01-31T17:48:52Z 12372 TID-ko1ws APN::Jobs::SidekiqNotificationJob JID-11111 INFO: start
Enter PEM pass phrase:

Race condition for creating ConnectionPool?

https://github.com/arthurnn/apn_sender/blob/master/lib/apn/connection.rb#L5

As you probably know, Sidekiq runs in a multithreaded environment. I believe calling APN.notify_sync from multiple threads will still cause a race condition for the conditional assignment and creation of the client.

    def connection_pool
      @pool ||= ConnectionPool.new(size: (pool_size || 1), timeout: (pool_timeout || 5)) do
        APN::Client.new(host: host,
                        port: port,
                        certificate: certificate,
                        password: password)
      end
    end

    def with_connection(&block)
      connection_pool.with(&block)
    end

APN::Client itself is already lazily opening a socket connect, so in my opinion it does not hurt to initialize the ConnectionPool in an earlier stage.

Another interesting idea is to see if the APN::Client could be a Celluloid::Actor of its own.

allow configuring pid file location

Heroku (and localhost if tmp/pids is not present) bombs:

 /app/vendor/bundle/ruby/1.9.1/gems/daemons-1.1.9/lib/daemons/pidfile.rb:94:in `initialize': No such file or directory - /app/tmp/pids/apn_sender.pid (Errno::ENOENT)

Resque workers fail to run

Let's look at the example from apn/tasks:

APN.password = ENV['CERT_PASS']
APN.full_certificate_path =  ENV['FULL_CERT_PATH']
APN.logger = Rails.logger

worker = ::Resque::Worker.new(APN::Jobs::QUEUE_NAME)

puts "*** Starting worker to send apple notifications in the background from #{worker}"
worker.work(ENV['INTERVAL'] || 5) # interval, will block

This starts just fine. However...

  • require 'apn/jobs/resque_notification_job' is now run inside the backend method.
  • The queue uses the job class APN::Jobs::ResqueNotificationJob.

So, since we've never called APN.backend in this worker (and we shouldn't have to do that!), the first queue item will crash when attempting to reference the un-loaded ResqueNotificationJob class.

I think the solution should load the job classes up front, rather than requiring a backend (which the workers won't even use) to be created.

How do you feel about trying to require both sidekiq and resque, like below?

begin
  require 'sidekiq'
  require 'apn/jobs/sidekiq_notification_job'
rescue LoadError
end

begin
  require 'resque'
  require 'apn/jobs/resque_notification_job'
rescue LoadError
end

This solves my problems, although it feels a little less than clean. If it's fine with you, I'll send a pull request.

Can't generate the apn_sender daemon

Hey,
I'm been trying to generate the apn_sender daemon script this way:

./script/generate apn_sender

but it returns "zsh: no such file or directory: ./script/generate"

Using the rails way to generate stuffs:

rails g apn_sender

returns "Could not find generator apn_sender."

Rails version: Rails 3.2.13
ruby version: ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-darwin12.0.0]

Do you have an idea why it doesn't work?
Thanks in advance for your help

This lib with Resque creates new connection to APNS each time

You wrote in README

sends them along to Apple over a single, persistent socket

but with Resque it creates new connection and new client object each time

D, [2013-11-21T13:05:55.474087 #17156] DEBUG -- : 2013-11-21 13:05:55 +0200: Connecting to gateway.push.apple.com:2195...
D, [2013-11-21T13:05:55.474160 #17156] DEBUG -- : 2013-11-21 13:05:55 +0200: #<APN::Client:0x007f3d00d888a0>
D, [2013-11-21T13:05:56.206347 #17156] DEBUG -- : 2013-11-21 13:05:56 +0200: Message sent.

...

D, [2013-11-21T13:06:01.213870 #17168] DEBUG -- : 2013-11-21 13:06:01 +0200: Connecting to gateway.push.apple.com:2195...
D, [2013-11-21T13:06:01.214269 #17168] DEBUG -- : 2013-11-21 13:06:01 +0200: #<APN::Client:0x007f3d00dba530>
D, [2013-11-21T13:06:01.807808 #17168] DEBUG -- : 2013-11-21 13:06:01 +0200: Message sent.

Maybe I'm doing something wrong, but can you explain this behavior?

using old resque interface

Try to used the apn_sender from rubygems. Got following error. install apn_sender from github works for me. time for a release?

*** DEPRECATION WARNING: Resque::Worker#verbose and #very_verbose are deprecated. Please set Resque.logger.level instead
Called from: /home/ /.rvm/gems/ruby-1.9.3-p429/gems/resque-1.24.1/lib/resque/worker.rb:649:in verbose=' /home/ /.rvm/gems/ruby-1.9.3-p429/gems/apn_sender-1.0.6/lib/apn/tasks.rb:12:inblock (2 levels) in <top (required)>'
/home/ /.rvm/gems/ruby-1.9.3-p429@global/gems/rake-10.0.4/lib/rake/task.rb:246:in call' /home/ /.rvm/gems/ruby-1.9.3-p429@global/gems/rake-10.0.4/lib/rake/task.rb:246:inblock in execute'
/home/ /.rvm/gems/ruby-1.9.3-p429@global/gems/rake-10.0.4/lib/rake/task.rb:241:in each' /home/ /.rvm/gems/ruby-1.9.3-p429@global/gems/rake-10.0.4/lib/rake/task.rb:241:inexecute'
*** Starting worker to send apple notifications in the background from ip-10-48-241-94:3349:apple_push_notifications
WARNING: This way of doing signal handling is now deprecated. Please see http://hone.heroku.com/resque/2012/08/21/resque-signals.html for more info.
I, [2013-06-24T14:11:59.538314 #3349] INFO -- : 2013-06-24 14:11:59 +0000: Running before_first_fork hook
I, [2013-06-24T14:11:59.538823 #3349] INFO -- : 2013-06-24 14:11:59 +0000: Failed to start worker : #<NoMethodError: undefined method call' for []:Array> rake aborted! wrong number of arguments (1 for 0) /home//.rvm/gems/ruby-1.9.3-p429/gems/apn_sender-1.0.6/lib/resque/hooks/before_unregister_worker.rb:10:in unregister_worker'
/home/ /.rvm/gems/ruby-1.9.3-p429/gems/resque-1.24.1/lib/resque/worker.rb:176:inrescue in work' /home//.rvm/gems/ruby-1.9.3-p429/gems/resque-1.24.1/lib/resque/worker.rb:130:in work'
/home//.rvm/gems/ruby-1.9.3-p429/gems/apn_sender-1.0.6/lib/apn/tasks.rb:17:inblock (2 levels) in <top (required)>' /home//.rvm/gems/ruby-1.9.3-p429@global/gems/rake-10.0.4/lib/rake/task.rb:246:in call'
/home//.rvm/gems/ruby-1.9.3-p429@global/gems/rake-10.0.4/lib/rake/task.rb:246:inblock in execute' /home//.rvm/gems/ruby-1.9.3-p429@global/gems/rake-10.0.4/lib/rake/task.rb:241:in each'
/home//.rvm/gems/ruby-1.9.3-p429@global/gems/rake-10.0.4/lib/rake/task.rb:241:inexecute' /home//.rvm/gems/ruby-1.9.3-p429@global/gems/rake-10.0.4/lib/rake/task.rb:184:in block in invoke_with_call_chain'
/home//.rvm/gems/ruby-1.9.3-p429@global/gems/rake-10.0.4/lib/rake/task.rb:177:ininvoke_with_call_chain' /home//.rvm/gems/ruby-1.9.3-p429@global/gems/rake-10.0.4/lib/rake/task.rb:170:in invoke'
/home//.rvm/gems/ruby-1.9.3-p429@global/gems/rake-10.0.4/lib/rake/application.rb:143:ininvoke_task' /home//.rvm/gems/ruby-1.9.3-p429@global/gems/rake-10.0.4/lib/rake/application.rb:101:in block (2 levels) in top_level'
/home//.rvm/gems/ruby-1.9.3-p429@global/gems/rake-10.0.4/lib/rake/application.rb:101:ineach' /home//.rvm/gems/ruby-1.9.3-p429@global/gems/rake-10.0.4/lib/rake/application.rb:101:in block in top_level'
/home//.rvm/gems/ruby-1.9.3-p429@global/gems/rake-10.0.4/lib/rake/application.rb:110:inrun_with_threads' /home//.rvm/gems/ruby-1.9.3-p429@global/gems/rake-10.0.4/lib/rake/application.rb:95:in top_level'
/home//.rvm/gems/ruby-1.9.3-p429@global/gems/rake-10.0.4/lib/rake/application.rb:73:inblock in run' /home//.rvm/gems/ruby-1.9.3-p429@global/gems/rake-10.0.4/lib/rake/application.rb:160:in standard_exception_handling'
/home//.rvm/gems/ruby-1.9.3-p429@global/gems/rake-10.0.4/lib/rake/application.rb:70:inrun' /home//.rvm/gems/ruby-1.9.3-p429/bin/ruby_noexec_wrapper:14:in eval'
/home//.rvm/gems/ruby-1.9.3-p429/bin/ruby_noexec_wrapper:14:in`

'
Tasks: TOP => apn:sender
(See full trace by running task with --trace)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.