Class: WebSocketClient
- Inherits:
-
Object
- Object
- WebSocketClient
- Defined in:
- lib/javonet-ruby-sdk/core/web_socket_client/web_socket_client.rb
Class Method Summary collapse
- .add_or_get_client(url) ⇒ Object
- .close_client(url, suppress_close_frame: false) ⇒ Object
-
.get_state(url) ⇒ Object
Returns :OPEN, :CLOSED, or nil (no client).
- .send_message(url, message) ⇒ Object
Instance Method Summary collapse
- #close(suppress_close_frame: false) ⇒ Object
-
#initialize(url) ⇒ WebSocketClient
constructor
A new instance of WebSocketClient.
- #open? ⇒ Boolean
-
#receive_message(timeout: 5) ⇒ Object
Returns Array<Integer> (bytes) or nil on timeout.
-
#send_message(message) ⇒ Object
message can be an Array<Integer 0..255> or a String of bytes.
Constructor Details
#initialize(url) ⇒ WebSocketClient
Returns a new instance of WebSocketClient.
43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/javonet-ruby-sdk/core/web_socket_client/web_socket_client.rb', line 43 def initialize(url) @uri = URI.parse(url) raise "Only ws:// or wss:// URLs are supported" unless %w[ws wss].include?(@uri.scheme) @host = @uri.host @port = @uri.port || default_port @path = (@uri.path.nil? || @uri.path.empty?) ? '/' : @uri.path @path += "?#{@uri.query}" if @uri.query @socket = open_socket @handshake = WebSocket::Handshake::Client.new(url: url) # masks handled by client frames perform_handshake @incoming = WebSocket::Frame::Incoming::Client.new(version: @handshake.version) end |
Class Method Details
.add_or_get_client(url) ⇒ Object
8 9 10 |
# File 'lib/javonet-ruby-sdk/core/web_socket_client/web_socket_client.rb', line 8 def add_or_get_client(url) cache[url] && cache[url].open? ? cache[url] : (cache[url]&.close; cache[url] = new(url)) end |
.close_client(url, suppress_close_frame: false) ⇒ Object
24 25 26 27 |
# File 'lib/javonet-ruby-sdk/core/web_socket_client/web_socket_client.rb', line 24 def close_client(url, suppress_close_frame: false) cache[url]&.close(suppress_close_frame: suppress_close_frame) cache.delete(url) end |
.get_state(url) ⇒ Object
Returns :OPEN, :CLOSED, or nil (no client)
30 31 32 33 34 |
# File 'lib/javonet-ruby-sdk/core/web_socket_client/web_socket_client.rb', line 30 def get_state(url) c = cache[url] return nil unless c c.open? ? :OPEN : :CLOSED end |
.send_message(url, message) ⇒ Object
12 13 14 15 16 17 18 19 20 21 22 |
# File 'lib/javonet-ruby-sdk/core/web_socket_client/web_socket_client.rb', line 12 def (url, ) client = add_or_get_client(url) client.() return client. rescue Errno::EPIPE, Errno::ECONNRESET, Errno::ETIMEDOUT, IOError, SystemCallError # Socket likely died due to server idle-timeout; rebuild without sending CLOSE close_client(url, suppress_close_frame: true) client = add_or_get_client(url) client.() client. end |
Instance Method Details
#close(suppress_close_frame: false) ⇒ Object
129 130 131 132 133 134 135 136 137 |
# File 'lib/javonet-ruby-sdk/core/web_socket_client/web_socket_client.rb', line 129 def close(suppress_close_frame: false) begin send_close_reply unless suppress_close_frame || @socket.closed? rescue Errno::EPIPE, Errno::ECONNRESET, IOError, SystemCallError # peer already gone — ignore ensure @socket.close unless @socket.closed? end end |
#open? ⇒ Boolean
122 123 124 125 126 127 |
# File 'lib/javonet-ruby-sdk/core/web_socket_client/web_socket_client.rb', line 122 def open? return false if @socket.closed? true rescue IOError, SystemCallError false end |
#receive_message(timeout: 5) ⇒ Object
Returns Array<Integer> (bytes) or nil on timeout
77 78 79 80 81 82 83 84 85 86 87 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 118 119 120 |
# File 'lib/javonet-ruby-sdk/core/web_socket_client/web_socket_client.rb', line 77 def (timeout: 5) deadline = Time.now + timeout payload_chunks = [] loop do # try to parse any buffered data first if (frame = @incoming.next) case frame.type when :binary, :text, :continuation payload_chunks << frame.data return payload_chunks.join.bytes when :ping send_pong(frame.data) when :close send_close_reply return nil end next end # no complete frame yet—read from socket or timeout remaining = deadline - Time.now return nil if remaining <= 0 if IO.select([@socket], nil, nil, [remaining, 0.05].max) begin chunk = @socket.read_nonblock(64 * 1024, exception: false) case chunk when nil # peer closed return nil when :wait_readable next else @incoming << chunk end rescue IO::WaitReadable next rescue EOFError return nil end end end end |
#send_message(message) ⇒ Object
message can be an Array<Integer 0..255> or a String of bytes
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/javonet-ruby-sdk/core/web_socket_client/web_socket_client.rb', line 59 def () data = case when String then .b when Array then .pack('C*') else raise ArgumentError, "Unsupported message type: #{.class}" end frame = WebSocket::Frame::Outgoing::Client.new( version: @handshake.version, data: data, type: :binary ) write_all(frame.to_s) true end |