nomad-cli / houston

Apple Push Notifications; No Dirigible Required
http://nomad-cli.com
MIT License
2.93k stars 229 forks source link

Getting nil back with no errors when APN.push(notification) and notification is not delivered. #157

Closed hugoh59 closed 3 years ago

hugoh59 commented 6 years ago

I'm not sure why this is happening but I'm not getting any response back just nil and no error message.

aroc commented 6 years ago

@hugoh1995 Any update here? Have you figured this out? I'm encountering a potentially similar issue.

justindunn commented 6 years ago

I'm experiencing this issue now as well, it was working last week fine then all the sudden now its not.

scanferla commented 6 years ago

What is supposed to be returned when it pushes successfully? I'm also getting nil when I do APN.push(notification)

a2f0 commented 6 years ago

I am seeing this as well.

cyurtbil commented 6 years ago

I'm seeing this as well. And I checked my certificates and generated push kit token by my ios app, they are working with apn push -c ... on command line. When I'm trying to do with Houston it doesn't send the notification and returns nil.

notification.error is nil as well

class V1::APNSService

  def initialize(recipient)
    @@recipient = recipient
    @@apn = Houston::Client.production
    # Encrypted with aes 256 option, I've set the APN_CERTIFICATE_PASSPHRASE to correct value, I've checked
    @@apn.certificate = File.read("pem_file_name.pem")
    @@notification = Houston::Notification.new(device: @@recipient.device.voip_registration_id)
  end

  def set_options(notification_type, *args)
    case notification_type
    when PushNotificationType::VIDEO_CALLING
      meeting = args[0][:meeting]
      @@notification.alert = "Incoming video call"
      @@notification.badge = 1
      @@notification.category = PushNotificationType::VIDEO_CALLING
      @@notification.custom_data = {meeting_id: meeting.id}
    end
    return self
  end

  def send
    @@apn.push(@@notification)
  end
end

Any help on this one?

cyurtbil commented 6 years ago

The problem with my code was on the iOS side. I have been saving my code without spaces and closing brackets.

Please save your token at this format <ce8be627 2e43e855 16033e24 b4c28922 0eeda487 9c477160 b2545e95 b68b5969>

As soon as I fixed the token format, it started working.

dfabreguette commented 6 years ago

Got exact same problem ! Reformating token didn't work either... Does someone has an idea to debug ?

jeffmcfadden commented 6 years ago

I ran into this as well, and through a series of changes resolved it. Here's what seemed to make a difference:

  1. Push each notification by itself; don't push more than on at once:

all_these_notifications.each{ |notification| client.push([notification]) } instead of client.push([all_these_notifications])

  1. Ensure your production pem has a password on it. We generated ours via Fastlane and by default the production pem didn't have a password, and that seemed to be a problem.

I'd love more datapoints to see if either of these solve a problem for someone.

dfabreguette commented 6 years ago

I'll give a try to set a password to pem file, thanks @jeffmcfadden !

acruis commented 6 years ago

I found the underlying issue after using Houston::Connection instead of Houston::Client:

When APNS returns an error, this line does not detect the error.

The following code imitates the error detection process in Houston::Client.push(notification):

certificate = File.read('my_production_cert.pem')
# Note that I'm using the PROD cert with the sandbox (DEV) environment
# This is to deliberately trigger an error
connection = Houston::Connection.new(Houston::APPLE_DEVELOPMENT_GATEWAY_URI, certificate, nil) 
connection.open

# Same, using a PROD device token for sandbox
notification = Houston::Notification.new(device: my_production_device_token)
notification.alert = 'Hello, World!'
connection.write(notification.message)

# The following lines follow the implementation in Houston::Client
the_ssl = connection.ssl
read_sock, write_sock = IO.select([the_ssl], [the_ssl], [the_ssl], nil)

puts read_sock.length # Prints 0
if (read_sock && read_sock[0])
    puts "Detected error" # Should print this, but doesn't.
end

connection.close

As seen in the snippet contents, the IO.select call doesn't produce the error content, so no error is detected.

Instead, if we just try to read the error without checking that it exists:

certificate = File.read('my_production_cert.pem')
connection = Houston::Connection.new(Houston::APPLE_DEVELOPMENT_GATEWAY_URI, certificate, nil) 
connection.open
notification = Houston::Notification.new(device: my_production_device_token)
notification.alert = 'Hello, World!'
connection.write(notification.message)

# Follows error parsing in Houston::Client. See line 55 of Houston::Client.
puts connection.read(6, nil).unpack('ccN')

connection.close

Then the puts connection.read(6, nil).unpack('ccN') produces the output:

8
8
0

which is the expected result:

In other words, if you want to see the error result, use the raw Houston::Connection to send the notification.

acruis commented 6 years ago

From reading a bit further, this is the same problem as #72, and there already is PR #73 which attempts to deal with it using a timeout.

SunnyIzr commented 5 years ago

Any plans to fix this in the near future?

thorvn commented 5 years ago

I got exact same problem

matias09 commented 5 years ago

Hi To all, I got the same problems with Houston, I was sending push notifications with no response if the Token that I sent was valid or not.

Playing with the file "houston-2.4.0/lib/houston/client.rb\" I modified the method Push() as this:

    def push(*notifications)
      return if notifications.empty?

      notifications.flatten!

      Connection.open(@gateway_uri, @certificate, @passphrase) do |connection|
        ssl = connection.ssl

        notifications.each_with_index do |notification, index|
          next unless notification.kind_of?(Notification)
          next if notification.sent?
          next unless notification.valid?

          notification.id = index

          connection.write(notification.message)
          notification.mark_as_sent!

          ready = IO.select([ssl], nil, nil, 10)
          if !ready
            return true
          else
            error = connection.read(6)

            _command, status, index = error.unpack('ccN')
            notification.apns_error_code = status
            notification.mark_as_unsent!

            return false
          end
        end
      end
    end

Basically the main change is add a timeout on "IO.select()" and check if the return has something.

I hope this comment be useful Greetings