class ApnsGatling::Client
Constants
- DRAFT
Attributes
sandbox[R]
token[R]
token_maker[R]
Public Class Methods
new(team_id, auth_key_id, ecdsa_key, sandbox = false)
click to toggle source
# File lib/apns_gatling/apns_client.rb, line 14 def initialize(team_id, auth_key_id, ecdsa_key, sandbox = false) @token_maker = Token.new(team_id, auth_key_id, ecdsa_key) @sandbox = sandbox @mutex = Mutex.new @requests = {} @cv = ConditionVariable.new init_vars end
Public Instance Methods
close()
click to toggle source
# File lib/apns_gatling/apns_client.rb, line 204 def close exit_thread(@socket_thread) init_vars @connection = nil end
connection()
click to toggle source
connection
# File lib/apns_gatling/apns_client.rb, line 128 def connection @connection ||= HTTP2::Client.new.tap do |conn| conn.on(:frame) do |bytes| @mutex.synchronize do @socket.write bytes @socket.flush @first_data_sent = true end end end end
connection_error(e)
click to toggle source
# File lib/apns_gatling/apns_client.rb, line 55 def connection_error(e) @mutex.synchronize do @requests.values.map do | request | block = request[:block] response = request[:response] if block && response response.error_with("connection failed #{e}") block.call response end end @requests = {} @connection = nil end end
ensure_sent_before_receiving()
click to toggle source
# File lib/apns_gatling/apns_client.rb, line 182 def ensure_sent_before_receiving while !@first_data_sent sleep 0.01 end end
ensure_socket_open()
click to toggle source
scoket
# File lib/apns_gatling/apns_client.rb, line 141 def ensure_socket_open @mutex.synchronize do return if @socket_thread @socket = new_socket @socket_thread = Thread.new do begin socket_loop rescue EOFError init_vars connection_error(SocketError.new('Socket was remotely closed')) rescue Exception => e init_vars connection_error(e) end end end end
exit_thread(thread)
click to toggle source
# File lib/apns_gatling/apns_client.rb, line 210 def exit_thread(thread) return unless thread thread.exit thread.join end
host()
click to toggle source
# File lib/apns_gatling/apns_client.rb, line 47 def host if sandbox APPLE_DEVELOPMENT_SERVER else APPLE_PRODUCTION_SERVER end end
init_vars()
click to toggle source
# File lib/apns_gatling/apns_client.rb, line 23 def init_vars @mutex.synchronize do @socket.close if @socket && !@socket.closed? @socket = nil @socket_thread = nil @first_data_sent = false @token_generated_at = 0 @blocking = true end end
join()
click to toggle source
# File lib/apns_gatling/apns_client.rb, line 216 def join @socket_thread.join if @socket_thread end
new_socket()
click to toggle source
# File lib/apns_gatling/apns_client.rb, line 159 def new_socket ctx = OpenSSL::SSL::SSLContext.new ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE # For ALPN support, Ruby >= 2.3 and OpenSSL >= 1.0.2 are required ctx.alpn_protocols = [DRAFT] ctx.alpn_select_cb = lambda do |protocols| DRAFT if protocols.include? DRAFT end tcp = TCPSocket.new(host, 443) socket = OpenSSL::SSL::SSLSocket.new(tcp, ctx) socket.sync_close = true socket.hostname = host socket.connect if socket.alpn_protocol != DRAFT puts "Failed to negotiate #{DRAFT} via ALPN" exit end socket end
provider_token()
click to toggle source
# File lib/apns_gatling/apns_client.rb, line 34 def provider_token timestamp = Time.new.to_i if timestamp - @token_generated_at > 3550 @mutex.synchronize do @token_generated_at = timestamp @token = @token_maker.new_token end @token else @token end end
push(message, &block)
click to toggle source
push message
# File lib/apns_gatling/apns_client.rb, line 71 def push(message, &block) request = Request.new(message, provider_token, host) response = Response.new(message) @mutex.synchronize do @requests[request.id] = {block: block, response: response} end begin ensure_socket_open stream = connection.new_stream rescue SocketError => e response.error_with("create connection failed #{e}") block.call response return rescue HTTP2::Error::StreamLimitExceeded response.error_with('stream limit exceeded') block.call response return rescue HTTP2::Error::ConnectionClosed close response.error_with('connection closed') block.call response return rescue StandardError => e close response.error_with("standard error #{e}") block.call response return end stream.on(:close) do @mutex.synchronize do @requests.delete request.id @token_generated_at = 0 if response.status == '403' && response.error[:reason] == 'ExpiredProviderToken' if @blocking @blocking = false @cv.signal end block.call response if block end end stream.on(:headers) do |h| hs = Hash[*h.flatten] response.headers.merge!(hs) end stream.on(:data) do |d| response.data << d end stream.headers(request.headers, end_stream: false) stream.data(request.data) @mutex.synchronize { @cv.wait(@mutex, 60) } if @blocking end
socket_loop()
click to toggle source
# File lib/apns_gatling/apns_client.rb, line 188 def socket_loop ensure_sent_before_receiving loop do begin data = @socket.read_nonblock(1024) connection << data # in rescue IO::WaitReadable IO.select([@socket]) retry rescue IO::WaitWritable IO.select(nil, [@socket]) retry end end end