Class: RubyLens::Clip::WebSocketChannel

Inherits:
Object
  • Object
show all
Defined in:
lib/rubylens/clip/web_socket_channel.rb

Overview

Minimal RFC 6455 client for Chrome's local DevTools WebSocket endpoint, so clip rendering needs no gem or Node dependencies. Client frames are masked text; ping, close, and fragmented server frames are handled; extensions are never negotiated.

Constant Summary collapse

HANDSHAKE_GUID =
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
TEXT_OPCODE =
1
CONTINUATION_OPCODE =
0
CLOSE_OPCODE =
8
PING_OPCODE =
9
PONG_OPCODE =
10

Instance Method Summary collapse

Constructor Details

#initialize(host, port, path, timeout: 15) ⇒ WebSocketChannel

Returns a new instance of WebSocketChannel.



24
25
26
27
28
29
# File 'lib/rubylens/clip/web_socket_channel.rb', line 24

def initialize(host, port, path, timeout: 15)
  @socket = TCPSocket.new(host, port)
  @socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
  @buffer = +"".b
  handshake(host, port, path, timeout)
end

Instance Method Details

#closeObject



58
59
60
61
62
# File 'lib/rubylens/clip/web_socket_channel.rb', line 58

def close
  @socket.close unless @socket.closed?
rescue IOError
  nil
end

#read_text(timeout: 30) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/rubylens/clip/web_socket_channel.rb', line 44

def read_text(timeout: 30)
  deadline = DeadlineIO.deadline(timeout)
  message = +"".b
  loop do
    finished, opcode, payload = read_frame(deadline)
    case opcode
    when TEXT_OPCODE, CONTINUATION_OPCODE then message << payload
    when CLOSE_OPCODE then raise Error, "Chrome closed the DevTools connection"
    when PING_OPCODE then send_control(PONG_OPCODE, payload)
    end
    return message.force_encoding(Encoding::UTF_8) if finished && opcode <= TEXT_OPCODE && !message.empty?
  end
end

#send_text(payload) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/rubylens/clip/web_socket_channel.rb', line 31

def send_text(payload)
  bytes = payload.b
  length = bytes.bytesize
  header = if length < 126 then [0x80 | TEXT_OPCODE, 0x80 | length].pack("CC")
           elsif length < 65_536 then [0x80 | TEXT_OPCODE, 0x80 | 126, length].pack("CCn")
           else [0x80 | TEXT_OPCODE, 0x80 | 127, length].pack("CCQ>")
           end
  mask = SecureRandom.bytes(4)
  @socket.write(header + mask + apply_mask(bytes, mask))
rescue SystemCallError, IOError => error
  raise Error, "lost the Chrome DevTools connection: #{error.message}"
end