Class: ORTC::OrtcClient

Inherits:
Object
  • Object
show all
Defined in:
lib/ortc.rb

Overview

A class representing an ORTC Client

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeOrtcClient

Creates a new instance of OrtcClient



275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# File 'lib/ortc.rb', line 275

def initialize
  @app_key = nil
  @auth_token = nil
  @ortcserver = nil
  @url = nil
  @is_connected = false
  @is_connecting = false
  @is_disconnecting = false
  @is_reconnecting = false
  @str_set = [('a'..'z'),('0'..'9')].map{|i| i.to_a}.flatten
  @session_id = (0...16).map{@str_set[rand(@str_set.length)]}.join
  @on_connected_callback = nil
  @on_disconnected_callback =nil
  @on_reconnected_callback = nil
  @on_reconnecting_callback = nil
  @on_subscribed_callback = nil
  @on_unsubscribed_callback = nil
  @on_exception_callback = nil
  @permissions_table = Hash.new
  @client_thread = nil
  @heartbeat_thread = nil
  @reconnect_thread = nil
  @channels = Hash.new
  @messages_buffer = Hash.new
  @socket = nil
  @got_heartbeat = false
  EM.error_handler{ |e| 
    raise "Error during event loop: #{e.message}"
    EM.stop_event_loop				
  }		
  Thread.abort_on_exception=true
end

Instance Attribute Details

#announcement_subchannelObject

The client announcement subchannel



260
261
262
# File 'lib/ortc.rb', line 260

def announcement_subchannel
  @announcement_subchannel
end

#cluster_urlObject

The cluster server URL



264
265
266
# File 'lib/ortc.rb', line 264

def cluster_url
  @cluster_url
end

#connection_metadataObject

The client connection metadata



262
263
264
# File 'lib/ortc.rb', line 262

def 
  @connection_metadata
end

#idObject

The client identifier



268
269
270
# File 'lib/ortc.rb', line 268

def id
  @id
end

#is_connectedObject (readonly)

Indicates whether the client is connected



272
273
274
# File 'lib/ortc.rb', line 272

def is_connected
  @is_connected
end

#session_idObject (readonly)

The client session identifier



270
271
272
# File 'lib/ortc.rb', line 270

def session_id
  @session_id
end

#urlObject

The server URL



266
267
268
# File 'lib/ortc.rb', line 266

def url
  @url
end

Instance Method Details

#connect(application_key, authentication_token = 'PM.Anonymous') ⇒ Object

Connects the client using the supplied application key and authentication token.

  • application_key - Your ORTC application key.

  • authentication_token - Your ORTC authentication token, this parameter is optional.

Usage: ortc_client.connect ‘aBc123’ ortc_client.connect ‘aBc123’, ‘au7h3n71ca710nT0k3n’



530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
# File 'lib/ortc.rb', line 530

def connect(application_key, authentication_token='PM.Anonymous')
  begin
    @on_exception_callback.call(self, "Metadata exceeds the limit of #{MAX_CONNECTION_METADATA_SIZE} bytes") if(@on_exception_callback)	
    return
  end if @connection_metadata.bytes.to_a.size > MAX_CONNECTION_METADATA_SIZE if @connection_metadata != nil
  begin 
    @on_exception_callback.call(self, 'Wrong Applicaition Key') if(@on_exception_callback)	
    return 
  end if (!application_key.is_a?(String) || application_key.size < 1)			
  $1 if application_key =~ /^[\w\-:\/.]+$/ or begin @on_exception_callback.call(self, "Application key: \"#{application_key}\" has invalid characters") if (@on_exception_callback)
                                                return end					
  $1 if authentication_token =~ /^[\w\-:\/.]+$/ or begin @on_exception_callback.call(self, "Authentication token: \"#{authentication_token}\" has invalid characters") if (@on_exception_callback)
                                                     return end					
  begin 
    @on_exception_callback.call(self, 'Already connected') if(@on_exception_callback)	
    return 
  end if @is_connected 
  begin 
    @on_exception_callback.call(self, 'Already trying to connect') if(@on_exception_callback)	
    return 
  end if @is_connecting		
  begin
    @on_exception_callback.call(self, 'URL is empty') if(@on_exception_callback)	
    return 
  end if @cluster_url.to_s == '' && @url.to_s == ''
  
  @is_connecting = true
  @app_key = application_key
  @auth_token = authentication_token
  @client_thread = nil
  @client_thread = Thread.new {				
    if @url.to_s == ''
      $1 if @cluster_url=~ /^(http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/ or begin @on_exception_callback.call(self, "Invalid cluster URL") if (@on_exception_callback)
                                                                                                                  @client_thread.kill if @client_thread end
      @ortcserver = ORTC._get_cluster(@cluster_url)
    else
      $1 if @url=~ /^(http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/ or begin @on_exception_callback.call(self, "Invalid URL") if (@on_exception_callback)
                                                                                                          @client_thread.kill if @client_thread end
      @ortcserver = @url.clone
    end
    begin 
      @on_exception_callback.call(self, 'There is no server available') if @on_exception_callback
      @is_connecting = false
      @client_thread.kill if @client_thread
    end if @ortcserver.to_s == ''
    @ortcserver["http"]="ws" if @ortcserver["http"]
    conn_str =  "#{@ortcserver}/broadcast/#{rand(1000)}/#{(0...8).map{@str_set[rand(@str_set.length)]}.join}/websocket"

    EM.run {					
      @socket = Faye::WebSocket::Client.new(conn_str)

      @socket.onopen = lambda do |event|
        
      end

      @socket.onmessage = lambda do |event|								
        if event.data.eql? "o"	
          EM.next_tick {
            @socket.send "validate;#{@app_key};#{@auth_token};#{@announcement_subchannel};#{@session_id};#{@connection_metadata}".to_json
          }
        elsif event.data != "h"
          parse_message event.data
        else
          @got_heartbeat = true
        end
      end
      
      @socket.onclose = lambda do |event|
        if(@is_disconnecting || @is_connecting)
          @on_disconnected_callback.call(self) if(@on_disconnected_callback && @is_disconnecting)								
          @is_disconnecting = false
          @is_connecting = false								
          unless @is_reconnecting
            @reconnect_thread.kill if @reconnect_thread
            @channels.clear
          end
          @heartbeat_thread.kill if @heartbeat_thread 
        end
        @is_connected = false
        EM.stop_event_loop
      end
    }
  }
  @client_thread.run
end

#disable_presence(private_key, channel, &block) ⇒ Object

Disables presence for the specified channel.

Note: This method will send your Private Key over the Internet. Make sure to use secure connection.

  • private_key - The private key provided when the ORTC service is purchased.

  • channel - Channel to disable presence.

  • &block - Callback with error and result parameters.

Usage: ortc_client.disable_presence(ortc_private_key, channel) { |error, result|

if error.to_s.empty?
    puts "result: #{result}"
else
    puts "error: #{error}"
end

}



410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
# File 'lib/ortc.rb', line 410

def disable_presence(private_key, channel, &block)
  if not @is_connected
    @on_exception_callback.call(self, 'Not connected') if(@on_exception_callback)	
    return false
  elsif @cluster_url.to_s == '' && @url.to_s == ''
    @on_exception_callback.call(self, 'URL is empty') if(@on_exception_callback)	
    return false
  else
    if not @url.to_s.empty?
      ORTC.disable_presence(@url, false, @app_key, private_key, channel, &block)
    else
      ORTC.disable_presence(@cluster_url, true, @app_key, private_key, channel, &block)
    end
  end
end

#disconnectObject

Disconnects the client.

Usage: ortc_client.disconnect



620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
# File 'lib/ortc.rb', line 620

def disconnect
  begin @on_exception_callback.call(self, 'Not connected') if(@on_exception_callback)
    return
  end unless @is_connected || @is_reconnecting
  if @is_reconnecting
    @reconnect_thread.kill if @reconnect_thread
    @heartbeat_thread.kill if @heartbeat_thread 
  end
  @is_connecting = false
  @is_reconnecting = false
  @is_disconnecting = true
  @channels.clear
  #@socket.close(nil, nil ,nil)		
  @socket.close
  @client_thread.kill if @client_thread
end

#enable_presence(private_key, channel, metadata, &block) ⇒ Object

Enables presence for the specified channel with first 100 unique metadata if true.

Note: This method will send your Private Key over the Internet. Make sure to use secure connection.

  • private_key - The private key provided when the ORTC service is purchased.

  • channel - Channel to activate presence.

  • metadata - Defines if to collect first 100 unique metadata.

  • &block - Callback with error and result parameters.

Usage: ortc_client.enable_presence(ortc_private_key, channel, true) { |error, result|

if error.to_s.empty?
    puts "result: #{result}"
else
    puts "error: #{error}"
end

}



378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
# File 'lib/ortc.rb', line 378

def enable_presence(private_key, channel, , &block)
  if not @is_connected
    @on_exception_callback.call(self, 'Not connected') if(@on_exception_callback)	
    return false
  elsif @cluster_url.to_s == '' && @url.to_s == ''
    @on_exception_callback.call(self, 'URL is empty') if(@on_exception_callback)	
    return false
  else
    if not @url.to_s.empty?
      ORTC.enable_presence(@url, false, @app_key, private_key, channel, , &block)
    else
      ORTC.enable_presence(@cluster_url, true, @app_key, private_key, channel, , &block)
    end
  end
end

#is_subscribed(channel) ⇒ Object

Indicates whether the client is subscribed to the supplied channel.

  • channel - The channel name.

Returns boolean - Indicates whether the client is subscribed to the supplied channel.

Usage: puts ortc_client.is_subscribed(‘blue’)



461
462
463
464
465
466
# File 'lib/ortc.rb', line 461

def is_subscribed(channel)
  return false unless is_channel_valid channel
  ch = @channels[channel]
  return false if ch == nil
  return ch.is_subscribed
end

#on_connected(&block) ⇒ Object

Sets the callback which occurs when the client connects.

  • block - code to be interpreted when the client connects.

Usage: ortc_client.on_connected do |sender|

p [:Connected]

end



474
# File 'lib/ortc.rb', line 474

def on_connected(&block) @on_connected_callback = block end

#on_disconnected(&block) ⇒ Object

Sets the callback which occurs when the client disconnects.

  • block - code to be interpreted when the client disconnects.

Usage: ortc_client.on_disconnected do |sender|

p [:Disconnected]

end



482
# File 'lib/ortc.rb', line 482

def on_disconnected(&block) @on_disconnected_callback = block end

#on_exception(&block) ⇒ Object

Sets the callback which occurs when there is an exception.

  • block - code to be interpreted when there is an exception.

Usage: ortc_client.on_exception do |sender, exception|

p [:Exception, exception]

end



522
# File 'lib/ortc.rb', line 522

def on_exception(&block) @on_exception_callback = block end

#on_reconnected(&block) ⇒ Object

Sets the callback which occurs when the client reconnects.

  • block - code to be interpreted when the client reconnects.

Usage: ortc_client.on_reconnected do |sender|

p [:Reconnected]

end



490
# File 'lib/ortc.rb', line 490

def on_reconnected(&block) @on_reconnected_callback = block end

#on_reconnecting(&block) ⇒ Object

Sets the callback which occurs when the client attempts to reconnect.

  • block - code to be interpreted when the client attempts to reconnect.

Usage: ortc_client.on_reonnecting do |sender|

p [:Reconnecting]

end



498
# File 'lib/ortc.rb', line 498

def on_reconnecting(&block) @on_reconnecting_callback = block end

#on_subscribed(&block) ⇒ Object

Sets the callback which occurs when the client subscribes to a channel.

  • block - code to be interpreted when the client subscribes to a channel.

Usage: ortc_client.on_subscribed do |sender, channel|

p [:Subscribed, channel]

end



506
# File 'lib/ortc.rb', line 506

def on_subscribed(&block) @on_subscribed_callback = block end

#on_unsubscribed(&block) ⇒ Object

Sets the callback which occurs when the client unsubscribes from a channel.

  • block - code to be interpreted when the client unsubscribes from a channel.

Usage: ortc_client.on_unsubscribed do |sender, channel|

p [:Unsubscribed, channel]

end



514
# File 'lib/ortc.rb', line 514

def on_unsubscribed(&block) @on_unsubscribed_callback = block end

#presence(channel, &block) ⇒ Object

Gets a Hash table indicating the subscriptions in the specified channel and if active the first 100 unique metadata.

  • channel - Channel to presence data active.

  • &block - Callback with error and result parameters.

Usage: ortc_client.presence(channel) { |error, result|

if error.to_s.empty?
    puts "result: #{result}"
else
    puts "error: #{error}"
end

}



439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
# File 'lib/ortc.rb', line 439

def presence(channel, &block)
  if not @is_connected
    @on_exception_callback.call(self, 'Not connected') if(@on_exception_callback)	
    return false
  elsif @cluster_url.to_s == '' && @url.to_s == ''
    @on_exception_callback.call(self, 'URL is empty') if(@on_exception_callback)	
    return false
  else
    if not @url.to_s.empty?
      ORTC.presence(@url, false, @app_key, @auth_token, channel, &block)
    else
      ORTC.presence(@cluster_url, true, @app_key, @auth_token, channel, &block)
    end
  end
end

#save_authentication(url, is_cluster, authentication_token, is_private, application_key, time_to_live, private_key, channels_permissions) ⇒ Object

Saves the channel and its permissions for the supplied application key and authentication token.

Note: This method will send your Private Key over the Internet. Make sure to use secure connection.

  • url - The ORTC server URL.

  • is_cluster - Indicates whether the ORTC server is in a cluster.

  • authentication_token - The authentication token generated by an application server (for instance: a unique session ID).

  • is_private - Indicates whether the authentication token is private.

  • application_key - The application key provided when the ORTC service is purchased.

  • time_to_live - The authentication token time to live (TTL), in other words, the allowed activity time (in seconds).

  • private_key - The private key provided when the ORTC service is purchased.

  • channels_permissions - The hash table containing channels and their permissions (‘r’ - read, ‘w’ - write, ‘p’ - presence).

Returns boolean- Indicates whether the authentication was successful.

Usage: permissions = Hash.new permissions = ‘r’ permissions = ‘wp’ ortc_client.save_authentication(‘ortc-developers.realtime.co/server/ssl/2.1’, true, ‘Your_authentication_token’, false, ‘Your_app_key’, 1800, ‘Your_private_key’, permissions)



326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
# File 'lib/ortc.rb', line 326

def save_authentication(url, is_cluster, authentication_token, is_private, application_key, time_to_live, private_key, channels_permissions)
  unless channels_permissions.class == Hash
    @on_exception_callback.call(self, 'Wrong parameter: channels_permissions') if(@on_exception_callback)	
    return false
  end
  str = "AT=#{authentication_token}&AK=#{application_key}&PK=#{private_key}&TTL=#{time_to_live}&TP=#{channels_permissions.length}&PVT=#{is_private ? "1" : "0"}"
  channels_permissions.each {|channel, permission|
    return false unless is_channel_valid channel, false
    str << "&#{channel}=#{permission}"				
  }	
  if is_cluster
    auth_server = ORTC._get_cluster(url)
    begin
      @on_exception_callback.call(self, 'Can not connect with the server') if(@on_exception_callback)	
      return false 
    end if auth_server == ''
  end	
  auth_server = auth_server << (auth_server.match(/\/$/) ? 'authenticate' : '/authenticate')
  uri = URI.parse(auth_server)
  begin
    if http = Net::HTTP.new(uri.host, uri.port)
      if auth_server.match /^https/
        http.use_ssl = true
        http.verify_mode = OpenSSL::SSL::VERIFY_NONE
      end
      req = Net::HTTP::Post.new(uri.request_uri)
      req.body = str
      res = http.request(req)
    end
  rescue => e
    @on_exception_callback.call(self, e) if(@on_exception_callback)	
    return false
  end
  return res.code=='201' ? true : false
end

#send(channel, message) ⇒ Object

Sends the supplied message to the supplied channel.

  • channel - The channel name.

  • message - The message to send.

Usage: ortc_client.send(‘blue’, ‘This is a message’)



693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
# File 'lib/ortc.rb', line 693

def send(channel, message)
  return false unless is_channel_valid channel
  begin 
    @on_exception_callback.call(self, 'Message is null or empty') if(@on_exception_callback)	
    return 
  end if message.to_s.empty?
  unless @permissions_table.empty?
    hash = check_permissions 'send', channel 
    return false if hash == nil
  end
  message_id = (0...8).map{@str_set[rand(@str_set.length)]}.join
  #parts = message.bytes.to_a.each_slice(MAX_MESSAGE_SIZE).to_a
  parts = message.scan(/.{1,#{MAX_MESSAGE_SIZE}}/)
  EM.next_tick {
    parts.each_with_index { |part, index| 
      #@socket.send  "send;#{@app_key};#{@auth_token};#{channel};#{hash};#{message_id}_#{index+1}-#{parts.length}_#{part.pack('U*')}".to_json
      @socket.send  "send;#{@app_key};#{@auth_token};#{channel};#{hash};#{message_id}_#{index+1}-#{parts.length}_#{part}".to_json				       
    }				
  }
  return true
end

#subscribe(channel, subscribe_on_reconnect, &block) ⇒ Object

Subscribes to the supplied channel to receive messages sent to it.

  • channel - The channel name.

  • subscribeOnReconnected - Indicates whether the client should subscribe to the channel when reconnected (if it was previously subscribed when connected).

  • block - the callback called when a message arrives at the channel.

Usage: ortc_client.subscribe(‘blue’, true) { |sender, channel, message|

puts "Message received on (#{channel}): #{message}"

}



645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
# File 'lib/ortc.rb', line 645

def subscribe(channel, subscribe_on_reconnect, &block)
  return unless is_channel_valid channel
  ch = @channels[channel]
  if ch != nil
    begin 
      @on_exception_callback.call(self,  "Already subscribing to the channel #{channel}") if(@on_exception_callback)	
      return 
    end  if ch.is_subscribing
    begin 
      @on_exception_callback.call(self,  "Already subscribed to the channel #{channel}") if(@on_exception_callback)	
      return 
    end  if ch.is_subscribed
  end
  unless @permissions_table.empty?
    hash = check_permissions 'subscribe', channel 
    return if hash == nil
  end
  
  @channels[channel] = Channel.new(channel, block, true, subscribe_on_reconnect) unless ch
  EM.next_tick {
    @socket.send "subscribe;#{@app_key};#{@auth_token};#{channel};#{hash}".to_json
  }
end

#unsubscribe(channel) ⇒ Object

Unsubscribes from the supplied channel to stop receiving messages sent to it.

  • channel - The channel name.

Usage: ortc_client.unsubscribe(‘blue’)



673
674
675
676
677
678
679
680
681
682
683
684
685
686
# File 'lib/ortc.rb', line 673

def unsubscribe(channel)
  return unless is_channel_valid channel
  ch = @channels[channel]
  if ch != nil
    begin @on_exception_callback.call(self,  "Not subscribed to the channel #{channel}") if(@on_exception_callback)	
      return end  unless ch.is_subscribed
    ch.subscribe_on_reconnected = false
    EM.next_tick {
      @socket.send "unsubscribe;#{@app_key};#{channel}".to_json
    }
  else
    @on_exception_callback.call(self,  "Not subscribed to the channel #{channel}") if(@on_exception_callback)	
  end
end