Class: LeadZeppelin::APNS::Gateway

Inherits:
Object
  • Object
show all
Defined in:
lib/lead_zeppelin/apns/gateway.rb

Constant Summary collapse

HOST =
'gateway.push.apple.com'
SANDBOX_HOST =
'gateway.sandbox.push.apple.com'
PORT =
2195
DEFAULT_TIMEOUT =
10
DEFAULT_SELECT_WAIT =
0.3

Instance Method Summary collapse

Constructor Details

#initialize(ssl_context, opts = {}) ⇒ Gateway

Returns a new instance of Gateway.



11
12
13
14
15
16
17
# File 'lib/lead_zeppelin/apns/gateway.rb', line 11

def initialize(ssl_context, opts={})
  Logger.thread 'g'
  @semaphore = Mutex.new
  @ssl_context = ssl_context
  @opts        = opts
  connect
end

Instance Method Details

#connectObject



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/lead_zeppelin/apns/gateway.rb', line 19

def connect
  Logger.thread 's'
  begin
    timeout(@opts[:timeout] || DEFAULT_TIMEOUT) do
      socket = TCPSocket.new (@opts[:host] || HOST), (@opts[:port] || PORT)
      socket.setsockopt Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true

      ssl_socket = OpenSSL::SSL::SSLSocket.new socket, @ssl_context

      ssl_socket.sync_close = true # when ssl_socket is closed, make sure the regular socket closes too.

      ssl_socket.connect

      # FIXME TODO CHECK FOR EOFError HERE instead of in process_error

      @socket = socket
      @ssl_socket = ssl_socket
    end

    Logger.debug "gateway connection established"

  rescue Errno::ETIMEDOUT, Timeout::Error
    Logger.warn "gateway connection timeout, retrying"
    retry
  end
end

#disconnectObject



53
54
55
56
57
# File 'lib/lead_zeppelin/apns/gateway.rb', line 53

def disconnect
  Logger.info "disconnecting from gateway"
  Logger.thread 'd'
  @ssl_socket.close
end

#process_error(notification = nil) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/lead_zeppelin/apns/gateway.rb', line 59

def process_error(notification=nil)
  begin
    error_response = @ssl_socket.read_nonblock 6
    error = ErrorResponse.new error_response, notification

    Logger.warn "error: #{error.code}, #{error.identifier.inspect}, #{error.message}"
    Logger.thread 'e'

    reconnect

    if !@opts[:notification_error_block].respond_to?(:call)
      Logger.warn "You have not implemented an on_notification_error block. This could lead to your account being banned from APNS. See the APNS docs"
    else
      @opts[:notification_error_block].call(error)
    end

  rescue EOFError
    # FIXME put in a certificate error pre-check and perhaps an error block for handling this.
    # A better solution is the remove the application altogether from the client..
    # Sometimes this just means that the socket has disconnected. Apparently Apple does that too.
    #
    Logger.info "socket has closed for #{@opts[:application_identifier]}, reconnecting"
    reconnect
  rescue IO::WaitReadable
    # No data to read, continue
    Logger.thread 'g'
  end
end

#reconnectObject



46
47
48
49
50
51
# File 'lib/lead_zeppelin/apns/gateway.rb', line 46

def reconnect
  Logger.info "reconnecting to gateway"
  Logger.thread 'r'
  disconnect
  connect
end

#write(notification) ⇒ Object



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/lead_zeppelin/apns/gateway.rb', line 88

def write(notification)
  Logger.thread 'w'

  begin
    process_error

    @ssl_socket.write notification.payload

    read, write, error = IO.select [@ssl_socket], [], [@ssl_socket], (@opts[:select_wait] || DEFAULT_SELECT_WAIT)

    if !error.nil? && !error.first.nil?
      Logger.error "IO.select has reported an unexpected error. Reconnecting, sleeping a bit and retrying"
      sleep 1
      reconnect
    end

    if !read.nil? && !read.first.nil?
      process_error(notification)
      return false
    end

  rescue Errno::EPIPE => e
    Logger.warn 'gateway connection returned broken pipe, attempting reconnect and retrying'
    Logger.thread 'f'
    reconnect
    retry
  end

  true
end