Class: Pushr::Daemon::ApnsSupport::ConnectionApns

Inherits:
Object
  • Object
show all
Defined in:
lib/pushr/daemon/apns_support/connection_apns.rb

Constant Summary collapse

IDLE_PERIOD =
30 * 60
SELECT_TIMEOUT =
0.2
ERROR_TUPLE_BYTES =
6
APN_ERRORS =
{
  1 => 'Processing error',
  2 => 'Missing device token',
  3 => 'Missing topic',
  4 => 'Missing payload',
  5 => 'Missing token size',
  6 => 'Missing topic size',
  7 => 'Missing payload size',
  8 => 'Invalid token',
  255 => 'None (unknown error)'
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(configuration, i = nil) ⇒ ConnectionApns

Returns a new instance of ConnectionApns.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/pushr/daemon/apns_support/connection_apns.rb', line 24

def initialize(configuration, i = nil)
  @configuration = configuration
  if i
    # Apns push connection
    @name = "#{@configuration.app}: ConnectionApns #{i}"
    @host = "gateway.#{configuration.sandbox ? 'sandbox.' : ''}push.apple.com"
    @port = 2195
  else
    @name = "#{@configuration.app}: FeedbackReceiver"
    @host = "feedback.#{configuration.sandbox ? 'sandbox.' : ''}push.apple.com"
    @port = 2196
  end
  written
end

Instance Attribute Details

#configurationObject (readonly)

Returns the value of attribute configuration.



7
8
9
# File 'lib/pushr/daemon/apns_support/connection_apns.rb', line 7

def configuration
  @configuration
end

#last_writeObject

Returns the value of attribute last_write.



8
9
10
# File 'lib/pushr/daemon/apns_support/connection_apns.rb', line 8

def last_write
  @last_write
end

#nameObject (readonly)

Returns the value of attribute name.



7
8
9
# File 'lib/pushr/daemon/apns_support/connection_apns.rb', line 7

def name
  @name
end

Instance Method Details

#check_for_error(notification) ⇒ Object



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/pushr/daemon/apns_support/connection_apns.rb', line 90

def check_for_error(notification)
  # check for true, because check_for_error can be nil
  return if @configuration.skip_check_for_error == true

  if select(SELECT_TIMEOUT)
    error = nil

    if tuple = read(ERROR_TUPLE_BYTES)
      _, code, notification_id = tuple.unpack('ccN')

      description = APN_ERRORS[code.to_i] || 'Unknown error. Possible push bug?'
      error = Pushr::Daemon::DeliveryError.new(code, notification, description, 'APNS')
    else
      error = DisconnectionError.new
    end

    begin
      Pushr::Daemon.logger.error("[#{@name}] Error received, reconnecting...")
      reconnect
    ensure
      fail error if error
    end
  end
end

#closeObject



46
47
48
49
50
# File 'lib/pushr/daemon/apns_support/connection_apns.rb', line 46

def close
  @ssl_socket.close if @ssl_socket
  @tcp_socket.close if @tcp_socket
rescue IOError
end

#connectObject



39
40
41
42
43
44
# File 'lib/pushr/daemon/apns_support/connection_apns.rb', line 39

def connect
  @ssl_context = setup_ssl_context
  @tcp_socket, @ssl_socket = connect_socket
rescue
  Pushr::Daemon.logger.error("#{@name}] Error connection to server, invalid certificate?")
end

#read(num_bytes) ⇒ Object



52
53
54
# File 'lib/pushr/daemon/apns_support/connection_apns.rb', line 52

def read(num_bytes)
  @ssl_socket.read(num_bytes)
end

#reconnectObject



85
86
87
88
# File 'lib/pushr/daemon/apns_support/connection_apns.rb', line 85

def reconnect
  close
  @tcp_socket, @ssl_socket = connect_socket
end

#select(timeout) ⇒ Object



56
57
58
# File 'lib/pushr/daemon/apns_support/connection_apns.rb', line 56

def select(timeout)
  IO.select([@ssl_socket], nil, nil, timeout)
end

#write(data) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/pushr/daemon/apns_support/connection_apns.rb', line 60

def write(data)
  reconnect_idle if idle_period_exceeded?

  retry_count = 0

  begin
    write_data(data)
  rescue Errno::EPIPE, Errno::ETIMEDOUT, Errno::ECONNRESET, OpenSSL::SSL::SSLError => e
    retry_count += 1

    if retry_count == 1
      Pushr::Daemon.logger.error("[#{@name}] Lost connection to #{@host}:#{@port} (#{e.class.name}), reconnecting...")
    end

    if retry_count <= 3
      reconnect
      sleep 1
      retry
    else
      raise ConnectionError, "#{@name} tried #{retry_count - 1} times to reconnect but failed (#{e.class.name})."
    end
  end
  check_for_error(data)
end