rpush 7.0.1 → 9.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -4
  3. data/README.md +24 -44
  4. data/lib/generators/rpush_migration_generator.rb +1 -0
  5. data/lib/generators/templates/rpush.rb +23 -13
  6. data/lib/generators/templates/rpush_7_1_0_updates.rb +12 -0
  7. data/lib/rpush/client/active_model/apns/notification.rb +0 -4
  8. data/lib/rpush/client/active_model/{gcm → fcm}/app.rb +4 -3
  9. data/lib/rpush/client/active_model/{gcm → fcm}/expiry_collapse_key_mutual_inclusion_validator.rb +1 -1
  10. data/lib/rpush/client/active_model/fcm/notification.rb +129 -0
  11. data/lib/rpush/client/active_model/fcm/notification_keys_in_allowed_list_validator.rb +20 -0
  12. data/lib/rpush/client/active_model.rb +4 -3
  13. data/lib/rpush/client/active_record/{gcm → fcm}/app.rb +2 -2
  14. data/lib/rpush/client/active_record/{gcm → fcm}/notification.rb +2 -2
  15. data/lib/rpush/client/active_record.rb +2 -2
  16. data/lib/rpush/client/redis/app.rb +2 -0
  17. data/lib/rpush/client/redis/{gcm → fcm}/app.rb +2 -2
  18. data/lib/rpush/client/redis/{gcm → fcm}/notification.rb +2 -2
  19. data/lib/rpush/client/redis.rb +2 -2
  20. data/lib/rpush/configuration.rb +2 -19
  21. data/lib/rpush/daemon/apns2/delivery.rb +0 -1
  22. data/lib/rpush/daemon/apnsp8/delivery.rb +0 -1
  23. data/lib/rpush/daemon/fcm/delivery.rb +162 -0
  24. data/lib/rpush/daemon/{gcm.rb → fcm.rb} +1 -1
  25. data/lib/rpush/daemon/google_credential_cache.rb +41 -0
  26. data/lib/rpush/daemon/service_config_methods.rb +0 -2
  27. data/lib/rpush/daemon/store/active_record.rb +15 -12
  28. data/lib/rpush/daemon/store/interface.rb +3 -3
  29. data/lib/rpush/daemon/store/redis.rb +13 -9
  30. data/lib/rpush/daemon/webpush/delivery.rb +2 -2
  31. data/lib/rpush/daemon.rb +3 -9
  32. data/lib/rpush/reflection_collection.rb +3 -3
  33. data/lib/rpush/version.rb +2 -2
  34. data/lib/rpush.rb +1 -1
  35. data/spec/functional/apns2_spec.rb +2 -6
  36. data/spec/functional/cli_spec.rb +41 -15
  37. data/spec/functional/embed_spec.rb +57 -26
  38. data/spec/functional/{gcm_priority_spec.rb → fcm_priority_spec.rb} +13 -7
  39. data/spec/functional/fcm_spec.rb +77 -0
  40. data/spec/functional/retry_spec.rb +21 -4
  41. data/spec/functional/synchronization_spec.rb +1 -1
  42. data/spec/functional_spec_helper.rb +1 -7
  43. data/spec/spec_helper.rb +4 -1
  44. data/spec/support/active_record_setup.rb +3 -1
  45. data/spec/unit/client/active_record/{gcm → fcm}/app_spec.rb +2 -2
  46. data/spec/unit/client/active_record/fcm/notification_spec.rb +10 -0
  47. data/spec/unit/client/active_record/shared/app.rb +1 -1
  48. data/spec/unit/client/redis/fcm/app_spec.rb +5 -0
  49. data/spec/unit/client/redis/fcm/notification_spec.rb +5 -0
  50. data/spec/unit/client/shared/apns/notification.rb +0 -15
  51. data/spec/unit/client/shared/fcm/app.rb +4 -0
  52. data/spec/unit/client/shared/fcm/notification.rb +92 -0
  53. data/spec/unit/configuration_spec.rb +1 -1
  54. data/spec/unit/daemon/apnsp8/delivery_spec.rb +1 -1
  55. data/spec/unit/daemon/fcm/delivery_spec.rb +127 -0
  56. data/spec/unit/daemon/service_config_methods_spec.rb +1 -1
  57. data/spec/unit/daemon/shared/store.rb +0 -42
  58. data/spec/unit/daemon/wns/delivery_spec.rb +1 -1
  59. data/spec/unit/logger_spec.rb +1 -1
  60. data/spec/unit_spec_helper.rb +1 -1
  61. metadata +127 -69
  62. data/lib/rpush/apns_feedback.rb +0 -18
  63. data/lib/rpush/client/active_model/gcm/notification.rb +0 -62
  64. data/lib/rpush/daemon/apns/delivery.rb +0 -43
  65. data/lib/rpush/daemon/apns/feedback_receiver.rb +0 -91
  66. data/lib/rpush/daemon/apns.rb +0 -17
  67. data/lib/rpush/daemon/dispatcher/apns_tcp.rb +0 -152
  68. data/lib/rpush/daemon/dispatcher/tcp.rb +0 -22
  69. data/lib/rpush/daemon/gcm/delivery.rb +0 -241
  70. data/lib/rpush/daemon/tcp_connection.rb +0 -190
  71. data/spec/functional/apns_spec.rb +0 -162
  72. data/spec/functional/gcm_spec.rb +0 -46
  73. data/spec/functional/new_app_spec.rb +0 -44
  74. data/spec/unit/apns_feedback_spec.rb +0 -39
  75. data/spec/unit/client/active_record/gcm/notification_spec.rb +0 -14
  76. data/spec/unit/client/redis/gcm/app_spec.rb +0 -5
  77. data/spec/unit/client/redis/gcm/notification_spec.rb +0 -5
  78. data/spec/unit/client/shared/gcm/app.rb +0 -4
  79. data/spec/unit/client/shared/gcm/notification.rb +0 -77
  80. data/spec/unit/daemon/apns/delivery_spec.rb +0 -108
  81. data/spec/unit/daemon/apns/feedback_receiver_spec.rb +0 -137
  82. data/spec/unit/daemon/dispatcher/tcp_spec.rb +0 -32
  83. data/spec/unit/daemon/gcm/delivery_spec.rb +0 -387
  84. data/spec/unit/daemon/tcp_connection_spec.rb +0 -292
@@ -1,17 +0,0 @@
1
- module Rpush
2
- module Daemon
3
- module Apns
4
- extend ServiceConfigMethods
5
-
6
- HOSTS = {
7
- production: ['gateway.push.apple.com', 2195],
8
- development: ['gateway.sandbox.push.apple.com', 2195], # deprecated
9
- sandbox: ['gateway.sandbox.push.apple.com', 2195]
10
- }
11
-
12
- batch_deliveries true
13
- dispatcher :apns_tcp, host: proc { |app| HOSTS[app.environment.to_sym] }
14
- loops Rpush::Daemon::Apns::FeedbackReceiver, if: -> { Rpush.config.apns.feedback_receiver.enabled && !Rpush.config.push }
15
- end
16
- end
17
- end
@@ -1,152 +0,0 @@
1
- module Rpush
2
- module Daemon
3
- module Dispatcher
4
- class ApnsTcp < Rpush::Daemon::Dispatcher::Tcp
5
- include Loggable
6
- include Reflectable
7
-
8
- SELECT_TIMEOUT = 10
9
- ERROR_TUPLE_BYTES = 6
10
- APNS_ERRORS = {
11
- 1 => 'Processing error',
12
- 2 => 'Missing device token',
13
- 3 => 'Missing topic',
14
- 4 => 'Missing payload',
15
- 5 => 'Missing token size',
16
- 6 => 'Missing topic size',
17
- 7 => 'Missing payload size',
18
- 8 => 'Invalid device token',
19
- 10 => 'APNs closed connection (possible maintenance)',
20
- 255 => 'None (unknown error)'
21
- }
22
-
23
- def initialize(*args)
24
- super
25
- @dispatch_mutex = Mutex.new
26
- @stop_error_receiver = false
27
- @connection.on_connect { start_error_receiver }
28
- end
29
-
30
- def dispatch(payload)
31
- @dispatch_mutex.synchronize do
32
- @delivery_class.new(@app, @connection, payload.batch).perform
33
- record_batch(payload.batch)
34
- end
35
- end
36
-
37
- def cleanup
38
- if Rpush.config.push
39
- # In push mode only a single batch is sent, followed by immediate shutdown.
40
- # Allow the error receiver time to handle any errors.
41
- @reconnect_disabled = true
42
- sleep 1
43
- end
44
-
45
- @stop_error_receiver = true
46
- super
47
- @error_receiver_thread.join if @error_receiver_thread
48
- rescue StandardError => e
49
- log_error(e)
50
- reflect(:error, e)
51
- ensure
52
- @error_receiver_thread = nil
53
- end
54
-
55
- private
56
-
57
- def start_error_receiver
58
- @error_receiver_thread = Thread.new do
59
- check_for_error until @stop_error_receiver
60
- Rpush::Daemon.store.release_connection
61
- end
62
- end
63
-
64
- def delivered_buffer
65
- @delivered_buffer ||= RingBuffer.new(Rpush.config.batch_size * 10)
66
- end
67
-
68
- def record_batch(batch)
69
- batch.each_delivered do |notification|
70
- delivered_buffer << notification.id
71
- end
72
- end
73
-
74
- def check_for_error
75
- begin
76
- # On Linux, select returns nil from a dropped connection.
77
- # On OS X, Errno::EBADF is raised following a Errno::EADDRNOTAVAIL from the write call.
78
- return unless @connection.select(SELECT_TIMEOUT)
79
- tuple = @connection.read(ERROR_TUPLE_BYTES)
80
- rescue *TcpConnection::TCP_ERRORS
81
- reconnect unless @stop_error_receiver
82
- return
83
- end
84
-
85
- @dispatch_mutex.synchronize { handle_error_response(tuple) }
86
- rescue StandardError => e
87
- log_error(e)
88
- end
89
-
90
- def handle_error_response(tuple)
91
- if tuple
92
- _, code, notification_id = tuple.unpack('ccN')
93
- handle_error(code, notification_id)
94
- else
95
- handle_disconnect
96
- end
97
-
98
- if Rpush.config.push
99
- # Only attempt to handle a single error in Push mode.
100
- @stop_error_receiver = true
101
- return
102
- end
103
-
104
- reconnect
105
- ensure
106
- delivered_buffer.clear
107
- end
108
-
109
- def reconnect
110
- return if @reconnect_disabled
111
- log_error("Lost connection to #{@connection.host}:#{@connection.port}, reconnecting...")
112
- @connection.reconnect_with_rescue
113
- end
114
-
115
- def handle_disconnect
116
- log_error("The APNs disconnected before any notifications could be delivered. This usually indicates you are using an invalid certificate.") if delivered_buffer.size == 0
117
- end
118
-
119
- def handle_error(code, notification_id)
120
- notification_id = Rpush::Daemon.store.translate_integer_notification_id(notification_id)
121
- failed_pos = delivered_buffer.index(notification_id)
122
- description = description_for_code(code)
123
- log_error("Notification #{notification_id} failed with error: " + description)
124
- Rpush::Daemon.store.mark_ids_failed([notification_id], code, description, Time.now)
125
- reflect(:notification_id_failed, @app, notification_id, code, description)
126
-
127
- if failed_pos
128
- retry_ids = delivered_buffer[(failed_pos + 1)..-1]
129
- retry_notification_ids(retry_ids, notification_id)
130
- elsif delivered_buffer.size > 0
131
- log_error("Delivery sequence unknown for notifications following #{notification_id}.")
132
- end
133
- end
134
-
135
- def description_for_code(code)
136
- APNS_ERRORS[code.to_i] ? "#{APNS_ERRORS[code.to_i]} (#{code})" : "Unknown error code #{code.inspect}. Possible Rpush bug?"
137
- end
138
-
139
- def retry_notification_ids(ids, notification_id)
140
- return if ids.size == 0
141
-
142
- now = Time.now
143
- Rpush::Daemon.store.mark_ids_retryable(ids, now)
144
- notifications_str = 'Notification'
145
- notifications_str += 's' if ids.size > 1
146
- log_warn("#{notifications_str} #{ids.join(', ')} will be retried due to the failure of notification #{notification_id}.")
147
- ids.each { |id| reflect(:notification_id_will_retry, @app, id, now) }
148
- end
149
- end
150
- end
151
- end
152
- end
@@ -1,22 +0,0 @@
1
- module Rpush
2
- module Daemon
3
- module Dispatcher
4
- class Tcp
5
- def initialize(app, delivery_class, options = {})
6
- @app = app
7
- @delivery_class = delivery_class
8
- @host, @port = options[:host].call(@app)
9
- @connection = Rpush::Daemon::TcpConnection.new(@app, @host, @port)
10
- end
11
-
12
- def dispatch(payload)
13
- @delivery_class.new(@app, @connection, payload.notification, payload.batch).perform
14
- end
15
-
16
- def cleanup
17
- @connection.close if @connection
18
- end
19
- end
20
- end
21
- end
22
- end
@@ -1,241 +0,0 @@
1
- module Rpush
2
- module Daemon
3
- module Gcm
4
- # https://firebase.google.com/docs/cloud-messaging/server
5
- class Delivery < Rpush::Daemon::Delivery
6
- include MultiJsonHelper
7
-
8
- host = 'https://fcm.googleapis.com'
9
- FCM_URI = URI.parse("#{host}/fcm/send")
10
- UNAVAILABLE_STATES = %w(Unavailable BadGateway InternalServerError)
11
- INVALID_REGISTRATION_ID_STATES = %w(InvalidRegistration MismatchSenderId NotRegistered InvalidPackageName)
12
-
13
- def initialize(app, http, notification, batch)
14
- @app = app
15
- @http = http
16
- @notification = notification
17
- @batch = batch
18
- end
19
-
20
- def perform
21
- handle_response(do_post)
22
- rescue SocketError => error
23
- mark_retryable(@notification, Time.now + 10.seconds, error)
24
- raise
25
- rescue StandardError => error
26
- mark_failed(error)
27
- raise
28
- ensure
29
- @batch.notification_processed
30
- end
31
-
32
- protected
33
-
34
- def handle_response(response)
35
- case response.code.to_i
36
- when 200
37
- ok(response)
38
- when 400
39
- bad_request
40
- when 401
41
- unauthorized
42
- when 500
43
- internal_server_error(response)
44
- when 502
45
- bad_gateway(response)
46
- when 503
47
- service_unavailable(response)
48
- when 500..599
49
- other_5xx_error(response)
50
- else
51
- fail Rpush::DeliveryError.new(response.code.to_i, @notification.id, Rpush::Daemon::HTTP_STATUS_CODES[response.code.to_i])
52
- end
53
- end
54
-
55
- def ok(response)
56
- results = process_response(response)
57
- handle_successes(results.successes)
58
-
59
- if results.failures.any?
60
- handle_failures(results.failures, response)
61
- else
62
- mark_delivered
63
- log_info("#{@notification.id} sent to #{@notification.registration_ids.join(', ')}")
64
- end
65
- end
66
-
67
- def process_response(response)
68
- body = multi_json_load(response.body)
69
- results = Results.new(body['results'], @notification.registration_ids)
70
- results.process(invalid: INVALID_REGISTRATION_ID_STATES, unavailable: UNAVAILABLE_STATES)
71
- results
72
- end
73
-
74
- def handle_successes(successes)
75
- successes.each do |result|
76
- reflect(:gcm_delivered_to_recipient, @notification, result[:registration_id])
77
- next unless result.key?(:canonical_id)
78
- reflect(:gcm_canonical_id, result[:registration_id], result[:canonical_id])
79
- end
80
- end
81
-
82
- def handle_failures(failures, response)
83
- if failures[:unavailable].count == @notification.registration_ids.count
84
- retry_delivery(@notification, response)
85
- log_warn("All recipients unavailable. #{retry_message}")
86
- else
87
- if failures[:unavailable].any?
88
- unavailable_idxs = failures[:unavailable].map { |result| result[:index] }
89
- new_notification = create_new_notification(response, unavailable_idxs)
90
- failures.description += " #{unavailable_idxs.join(', ')} will be retried as notification #{new_notification.id}."
91
- end
92
- handle_errors(failures)
93
- fail Rpush::DeliveryError.new(nil, @notification.id, failures.description)
94
- end
95
- end
96
-
97
- def handle_errors(failures)
98
- failures.each do |result|
99
- reflect(:gcm_failed_to_recipient, @notification, result[:error], result[:registration_id])
100
- end
101
- failures[:invalid].each do |result|
102
- reflect(:gcm_invalid_registration_id, @app, result[:error], result[:registration_id])
103
- end
104
- end
105
-
106
- def create_new_notification(response, unavailable_idxs)
107
- attrs = { 'app_id' => @notification.app_id, 'collapse_key' => @notification.collapse_key, 'delay_while_idle' => @notification.delay_while_idle }
108
- registration_ids = @notification.registration_ids.values_at(*unavailable_idxs)
109
- Rpush::Daemon.store.create_gcm_notification(attrs, @notification.data,
110
- registration_ids, deliver_after_header(response), @app)
111
- end
112
-
113
- def bad_request
114
- fail Rpush::DeliveryError.new(400, @notification.id, 'GCM failed to parse the JSON request. Possibly an Rpush bug, please open an issue.')
115
- end
116
-
117
- def unauthorized
118
- fail Rpush::DeliveryError.new(401, @notification.id, 'Unauthorized, check your App auth_key.')
119
- end
120
-
121
- def internal_server_error(response)
122
- retry_delivery(@notification, response)
123
- log_warn("GCM responded with an Internal Error. " + retry_message)
124
- end
125
-
126
- def bad_gateway(response)
127
- retry_delivery(@notification, response)
128
- log_warn("GCM responded with a Bad Gateway Error. " + retry_message)
129
- end
130
-
131
- def service_unavailable(response)
132
- retry_delivery(@notification, response)
133
- log_warn("GCM responded with an Service Unavailable Error. " + retry_message)
134
- end
135
-
136
- def other_5xx_error(response)
137
- retry_delivery(@notification, response)
138
- log_warn("GCM responded with a 5xx Error. " + retry_message)
139
- end
140
-
141
- def deliver_after_header(response)
142
- Rpush::Daemon::RetryHeaderParser.parse(response.header['retry-after'])
143
- end
144
-
145
- def retry_delivery(notification, response)
146
- time = deliver_after_header(response)
147
- if time
148
- mark_retryable(notification, time)
149
- else
150
- mark_retryable_exponential(notification)
151
- end
152
- end
153
-
154
- def retry_message
155
- "Notification #{@notification.id} will be retried after #{@notification.deliver_after.strftime('%Y-%m-%d %H:%M:%S')} (retry #{@notification.retries})."
156
- end
157
-
158
- def do_post
159
- post = Net::HTTP::Post.new(FCM_URI.path, 'Content-Type' => 'application/json',
160
- 'Authorization' => "key=#{@app.auth_key}")
161
- post.body = @notification.as_json.to_json
162
- @http.request(FCM_URI, post)
163
- end
164
- end
165
-
166
- class Results
167
- attr_reader :successes, :failures
168
-
169
- def initialize(results_data, registration_ids)
170
- @results_data = results_data
171
- @registration_ids = registration_ids
172
- end
173
-
174
- def process(failure_partitions = {}) # rubocop:disable Metrics/AbcSize
175
- @successes = []
176
- @failures = Failures.new
177
- failure_partitions.each_key do |category|
178
- failures[category] = []
179
- end
180
-
181
- @results_data.each_with_index do |result, index|
182
- entry = {
183
- registration_id: @registration_ids[index],
184
- index: index
185
- }
186
- if result['message_id']
187
- entry[:canonical_id] = result['registration_id'] if result['registration_id'].present?
188
- successes << entry
189
- elsif result['error']
190
- entry[:error] = result['error']
191
- failures << entry
192
- failure_partitions.each do |category, error_states|
193
- failures[category] << entry if error_states.include?(result['error'])
194
- end
195
- end
196
- end
197
- failures.all_failed = failures.count == @registration_ids.count
198
- end
199
- end
200
-
201
- class Failures < Hash
202
- include Enumerable
203
- attr_writer :all_failed, :description
204
-
205
- def initialize
206
- super[:all] = []
207
- end
208
-
209
- def each
210
- self[:all].each { |x| yield x }
211
- end
212
-
213
- def <<(item)
214
- self[:all] << item
215
- end
216
-
217
- def description
218
- @description ||= describe
219
- end
220
-
221
- def any?
222
- self[:all].any?
223
- end
224
-
225
- private
226
-
227
- def describe
228
- if @all_failed
229
- error_description = "Failed to deliver to all recipients."
230
- else
231
- index_list = map { |item| item[:index] }
232
- error_description = "Failed to deliver to recipients #{index_list.join(', ')}."
233
- end
234
-
235
- error_list = map { |item| item[:error] }
236
- error_description + " Errors: #{error_list.join(', ')}."
237
- end
238
- end
239
- end
240
- end
241
- end
@@ -1,190 +0,0 @@
1
- module Rpush
2
- module Daemon
3
- class TcpConnectionError < StandardError; end
4
-
5
- class TcpConnection
6
- include Reflectable
7
- include Loggable
8
-
9
- OSX_TCP_KEEPALIVE = 0x10 # Defined in <netinet/tcp.h>
10
- KEEPALIVE_INTERVAL = 5
11
- KEEPALIVE_IDLE = 5
12
- KEEPALIVE_MAX_FAIL_PROBES = 1
13
- TCP_ERRORS = [SystemCallError, OpenSSL::OpenSSLError, IOError]
14
-
15
- attr_accessor :last_touch
16
- attr_reader :host, :port
17
-
18
- def self.idle_period
19
- 30.minutes
20
- end
21
-
22
- def initialize(app, host, port)
23
- @app = app
24
- @host = host
25
- @port = port
26
- @certificate = app.certificate
27
- @password = app.password
28
- @connected = false
29
- @connection_callbacks = []
30
- touch
31
- end
32
-
33
- def on_connect(&blk)
34
- raise 'already connected' if @connected
35
- @connection_callbacks << blk
36
- end
37
-
38
- def connect
39
- @ssl_context = setup_ssl_context
40
- @tcp_socket, @ssl_socket = connect_socket
41
- @connected = true
42
-
43
- @connection_callbacks.each do |blk|
44
- begin
45
- blk.call
46
- rescue StandardError => e
47
- log_error(e)
48
- end
49
- end
50
-
51
- @connection_callbacks.clear
52
- end
53
-
54
- def close
55
- @ssl_socket.close if @ssl_socket
56
- @tcp_socket.close if @tcp_socket
57
- rescue IOError # rubocop:disable HandleExceptions
58
- end
59
-
60
- def read(num_bytes)
61
- @ssl_socket.read(num_bytes) if @ssl_socket
62
- end
63
-
64
- def select(timeout)
65
- IO.select([@ssl_socket], nil, nil, timeout) if @ssl_socket
66
- end
67
-
68
- def write(data)
69
- connect unless @connected
70
- reconnect_idle if idle_period_exceeded?
71
-
72
- retry_count = 0
73
-
74
- begin
75
- write_data(data)
76
- rescue *TCP_ERRORS => e
77
- retry_count += 1
78
-
79
- if retry_count == 1
80
- log_error("Lost connection to #{@host}:#{@port} (#{e.class.name}, #{e.message}), reconnecting...")
81
- reflect(:tcp_connection_lost, @app, e)
82
- end
83
-
84
- if retry_count <= 3
85
- reconnect_with_rescue
86
- sleep 1
87
- retry
88
- else
89
- raise TcpConnectionError, "#{@app.name} tried #{retry_count - 1} times to reconnect but failed (#{e.class.name}, #{e.message})."
90
- end
91
- end
92
- end
93
-
94
- def reconnect_with_rescue
95
- reconnect
96
- rescue StandardError => e
97
- log_error(e)
98
- end
99
-
100
- def reconnect
101
- close
102
- @tcp_socket, @ssl_socket = connect_socket
103
- end
104
-
105
- protected
106
-
107
- def reconnect_idle
108
- log_info("Idle period exceeded, reconnecting...")
109
- reconnect
110
- end
111
-
112
- def idle_period_exceeded?
113
- Time.now - last_touch > self.class.idle_period
114
- end
115
-
116
- def write_data(data)
117
- @ssl_socket.write(data)
118
- @ssl_socket.flush
119
- touch
120
- end
121
-
122
- def touch
123
- self.last_touch = Time.now
124
- end
125
-
126
- def setup_ssl_context
127
- ssl_context = OpenSSL::SSL::SSLContext.new
128
- ssl_context.key = OpenSSL::PKey::RSA.new(@certificate, @password)
129
- ssl_context.cert = OpenSSL::X509::Certificate.new(@certificate)
130
- ssl_context
131
- end
132
-
133
- def connect_socket
134
- touch
135
- check_certificate_expiration
136
-
137
- tcp_socket = TCPSocket.new(@host, @port)
138
- tcp_socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)
139
- tcp_socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true)
140
-
141
- # Linux
142
- if [:SOL_TCP, :TCP_KEEPIDLE, :TCP_KEEPINTVL, :TCP_KEEPCNT].all? { |c| Socket.const_defined?(c) }
143
- tcp_socket.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPIDLE, KEEPALIVE_IDLE)
144
- tcp_socket.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL, KEEPALIVE_INTERVAL)
145
- tcp_socket.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT, KEEPALIVE_MAX_FAIL_PROBES)
146
- end
147
-
148
- # OSX
149
- if RUBY_PLATFORM =~ /darwin/
150
- tcp_socket.setsockopt(Socket::IPPROTO_TCP, OSX_TCP_KEEPALIVE, KEEPALIVE_IDLE)
151
- end
152
-
153
- ssl_socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, @ssl_context)
154
- ssl_socket.sync = true
155
- ssl_socket.connect
156
- [tcp_socket, ssl_socket]
157
- rescue *TCP_ERRORS => error
158
- if error.message =~ /certificate revoked/i
159
- log_error('Certificate has been revoked.')
160
- reflect(:ssl_certificate_revoked, @app, error)
161
- end
162
- raise TcpConnectionError, "#{error.class.name}, #{error.message}"
163
- end
164
-
165
- def check_certificate_expiration
166
- cert = @ssl_context.cert
167
- if certificate_expired?
168
- log_error(certificate_msg('expired'))
169
- raise Rpush::CertificateExpiredError.new(@app, cert.not_after)
170
- elsif certificate_expires_soon?
171
- log_warn(certificate_msg('will expire'))
172
- reflect(:ssl_certificate_will_expire, @app, cert.not_after)
173
- end
174
- end
175
-
176
- def certificate_msg(msg)
177
- time = @ssl_context.cert.not_after.utc.strftime('%Y-%m-%d %H:%M:%S UTC')
178
- "Certificate #{msg} at #{time}."
179
- end
180
-
181
- def certificate_expired?
182
- @ssl_context.cert.not_after && @ssl_context.cert.not_after.utc < Time.now.utc
183
- end
184
-
185
- def certificate_expires_soon?
186
- @ssl_context.cert.not_after && @ssl_context.cert.not_after.utc < (Time.now + 1.month).utc
187
- end
188
- end
189
- end
190
- end