Class: Tina4::WebSocketConnection
- Inherits:
-
Object
- Object
- Tina4::WebSocketConnection
- Defined in:
- lib/tina4/websocket.rb
Instance Attribute Summary collapse
-
#auth ⇒ Object
Returns the value of attribute auth.
-
#id ⇒ Object
readonly
Returns the value of attribute id.
-
#last_activity ⇒ Object
readonly
Returns the value of attribute last_activity.
-
#on_close_handler ⇒ Object
Returns the value of attribute on_close_handler.
-
#on_error_handler ⇒ Object
Returns the value of attribute on_error_handler.
-
#on_message_handler ⇒ Object
Returns the value of attribute on_message_handler.
-
#params ⇒ Object
Returns the value of attribute params.
-
#path ⇒ Object
Returns the value of attribute path.
-
#rooms ⇒ Object
readonly
Returns the value of attribute rooms.
Instance Method Summary collapse
-
#broadcast(message, include_self: false) ⇒ Object
Broadcast a message to all other connections on the same path.
- #broadcast_to_room(room_name, message, exclude_self: false) ⇒ Object
- #build_frame(opcode, data) ⇒ Object
- #close(code: 1000, reason: "") ⇒ Object
-
#closed? ⇒ Boolean
True once a write has failed (broken pipe / closed socket).
-
#initialize(id, socket, ws_server: nil, path: "/") ⇒ WebSocketConnection
constructor
A new instance of WebSocketConnection.
- #join_room(room_name) ⇒ Object
- #leave_room(room_name) ⇒ Object
-
#on_close(&block) ⇒ Object
Register a close handler (decorator style, matching Python).
-
#on_message(&block) ⇒ Object
Register a message handler (decorator style, matching Python).
- #read_frame ⇒ Object
- #send(message) ⇒ Object (also: #send_text)
-
#send_json(data) ⇒ Object
Serialize a Hash/Array (or any JSON-coercible value) and send as a text frame.
- #send_pong(data) ⇒ Object
-
#touch ⇒ Object
Mark inbound activity for the idle reaper.
Constructor Details
#initialize(id, socket, ws_server: nil, path: "/") ⇒ WebSocketConnection
Returns a new instance of WebSocketConnection.
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 |
# File 'lib/tina4/websocket.rb', line 661 def initialize(id, socket, ws_server: nil, path: "/") @id = id @socket = socket @params = {} @ws_server = ws_server @path = path # Verified JWT payload on a secured WS route, else nil (public route). # Mirrors Python's connection.auth. @auth = nil @rooms = Set.new @on_message_handler = nil @on_close_handler = nil @on_error_handler = nil # Updated on every inbound frame; the idle reaper closes connections that # have been silent longer than TINA4_WS_IDLE_TIMEOUT (opt-in). @last_activity = Time.now.to_f end |
Instance Attribute Details
#auth ⇒ Object
Returns the value of attribute auth.
658 659 660 |
# File 'lib/tina4/websocket.rb', line 658 def auth @auth end |
#id ⇒ Object (readonly)
Returns the value of attribute id.
657 658 659 |
# File 'lib/tina4/websocket.rb', line 657 def id @id end |
#last_activity ⇒ Object (readonly)
Returns the value of attribute last_activity.
657 658 659 |
# File 'lib/tina4/websocket.rb', line 657 def last_activity @last_activity end |
#on_close_handler ⇒ Object
Returns the value of attribute on_close_handler.
658 659 660 |
# File 'lib/tina4/websocket.rb', line 658 def on_close_handler @on_close_handler end |
#on_error_handler ⇒ Object
Returns the value of attribute on_error_handler.
658 659 660 |
# File 'lib/tina4/websocket.rb', line 658 def on_error_handler @on_error_handler end |
#on_message_handler ⇒ Object
Returns the value of attribute on_message_handler.
658 659 660 |
# File 'lib/tina4/websocket.rb', line 658 def @on_message_handler end |
#params ⇒ Object
Returns the value of attribute params.
658 659 660 |
# File 'lib/tina4/websocket.rb', line 658 def params @params end |
#path ⇒ Object
Returns the value of attribute path.
658 659 660 |
# File 'lib/tina4/websocket.rb', line 658 def path @path end |
#rooms ⇒ Object (readonly)
Returns the value of attribute rooms.
657 658 659 |
# File 'lib/tina4/websocket.rb', line 657 def rooms @rooms end |
Instance Method Details
#broadcast(message, include_self: false) ⇒ Object
Broadcast a message to all other connections on the same path
712 713 714 715 716 717 718 719 720 |
# File 'lib/tina4/websocket.rb', line 712 def broadcast(, include_self: false) return unless @ws_server @ws_server.connections.each do |cid, conn| next if !include_self && cid == @id next if conn.path != @path conn.send_text() end end |
#broadcast_to_room(room_name, message, exclude_self: false) ⇒ Object
704 705 706 707 708 709 |
# File 'lib/tina4/websocket.rb', line 704 def broadcast_to_room(room_name, , exclude_self: false) return unless @ws_server exclude = exclude_self ? @id : nil @ws_server.broadcast_to_room(room_name, , exclude: exclude) end |
#build_frame(opcode, data) ⇒ Object
790 791 792 |
# File 'lib/tina4/websocket.rb', line 790 def build_frame(opcode, data) Tina4.build_frame(opcode, data) end |
#close(code: 1000, reason: "") ⇒ Object
754 755 756 757 758 759 |
# File 'lib/tina4/websocket.rb', line 754 def close(code: 1000, reason: "") payload = [code].pack("n") + reason frame = build_frame(0x8, payload) @socket.write(frame) rescue nil @socket.close rescue nil end |
#closed? ⇒ Boolean
True once a write has failed (broken pipe / closed socket). The manager's resilient broadcast path uses this to prune dead connections.
724 725 726 |
# File 'lib/tina4/websocket.rb', line 724 def closed? @closed == true end |
#join_room(room_name) ⇒ Object
694 695 696 697 |
# File 'lib/tina4/websocket.rb', line 694 def join_room(room_name) @rooms.add(room_name) @ws_server&._join_room(@id, room_name) end |
#leave_room(room_name) ⇒ Object
699 700 701 702 |
# File 'lib/tina4/websocket.rb', line 699 def leave_room(room_name) @rooms.delete(room_name) @ws_server&._leave_room(@id, room_name) end |
#on_close(&block) ⇒ Object
Register a close handler (decorator style, matching Python).
690 691 692 |
# File 'lib/tina4/websocket.rb', line 690 def on_close(&block) @on_close_handler = block end |
#on_message(&block) ⇒ Object
Register a message handler (decorator style, matching Python).
685 686 687 |
# File 'lib/tina4/websocket.rb', line 685 def (&block) @on_message_handler = block end |
#read_frame ⇒ Object
761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 |
# File 'lib/tina4/websocket.rb', line 761 def read_frame first_byte = @socket.getbyte return nil unless first_byte opcode = first_byte & 0x0F second_byte = @socket.getbyte return nil unless second_byte masked = (second_byte & 0x80) != 0 length = second_byte & 0x7F if length == 126 length = @socket.read(2).unpack1("n") elsif length == 127 length = @socket.read(8).unpack1("Q>") end mask_key = masked ? @socket.read(4).bytes : nil data = @socket.read(length) || "" if masked && mask_key data = data.bytes.each_with_index.map { |b, i| b ^ mask_key[i % 4] }.pack("C*") end { opcode: opcode, data: data } rescue IOError, EOFError nil end |
#send(message) ⇒ Object Also known as: send_text
728 729 730 731 732 733 734 735 736 737 |
# File 'lib/tina4/websocket.rb', line 728 def send() data = .is_a?(String) ? : .to_s # Text frames must be valid UTF-8; binary payloads are sent verbatim. data = data.encode("UTF-8") if data.encoding != Encoding::ASCII_8BIT frame = build_frame(0x1, data) @socket.write(frame) rescue IOError # Connection closed — mark dead so the broadcast path prunes it. @closed = true end |
#send_json(data) ⇒ Object
Serialize a Hash/Array (or any JSON-coercible value) and send as a text frame. Matches Python/PHP send_json.
743 744 745 |
# File 'lib/tina4/websocket.rb', line 743 def send_json(data) send_text(JSON.generate(data)) end |
#send_pong(data) ⇒ Object
747 748 749 750 751 752 |
# File 'lib/tina4/websocket.rb', line 747 def send_pong(data) frame = build_frame(0xA, data || "") @socket.write(frame) rescue IOError @closed = true end |
#touch ⇒ Object
Mark inbound activity for the idle reaper.
680 681 682 |
# File 'lib/tina4/websocket.rb', line 680 def touch @last_activity = Time.now.to_f end |