Class: Utcp::Providers::WebSocketProvider

Inherits:
BaseProvider show all
Defined in:
lib/utcp/providers/websocket_provider.rb

Overview

Minimal RFC 6455 WebSocket client (text frames only) Supports ws:// and wss:// (TLS). Client frames are masked; server frames are unmasked.

Constant Summary collapse

GUID =
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11".freeze

Instance Attribute Summary

Attributes inherited from BaseProvider

#auth, #name, #type

Instance Method Summary collapse

Constructor Details

#initialize(name:, auth: nil) ⇒ WebSocketProvider



18
19
20
# File 'lib/utcp/providers/websocket_provider.rb', line 18

def initialize(name:, auth: nil)
  super(name: name, provider_type: "websocket", auth: auth)
end

Instance Method Details

#call_tool(tool, arguments = {}, &block) ⇒ Object

tool.provider expects: { “provider_type”:“websocket”, “url”:“ws(s)://host/path”, “message_template”: “…optional…” ,

"close_after_ms": 3000, "max_frames": 1 }

If a block is given, yields each received text frame (streaming).

Raises:



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/utcp/providers/websocket_provider.rb', line 30

def call_tool(tool, arguments = {}, &block)
  p = tool.provider
  uri = URI(Utils::Subst.apply(p["url"]))
  raise ConfigError, "WebSocket requires ws:// or wss:// URL" unless %w[ws wss].include?(uri.scheme)

  sock = connect_socket(uri)
  begin
    handshake(sock, uri)
    # Compose a text message to send
    payload = compose_payload(p, arguments)
    if payload
      send_text(sock, payload)
    end

    # Read frames; stop based on configuration
    close_after = (p["close_after_ms"] || 3000).to_i
    max_frames = (p["max_frames"] || 1).to_i
    deadline = Time.now + (close_after / 1000.0)
    frames = []
    while Time.now < deadline && frames.length < max_frames
      frame = recv_frame(sock, deadline: deadline)
      break unless frame
      case frame[:opcode]
      when 0x1 # text
        if block_given?
          yield frame[:payload]
        else
          frames << frame[:payload]
        end
      when 0x8 # close
        break
      when 0x9 # ping
        send_pong(sock, frame[:payload])
      when 0xA # pong
        # ignore
      else
        # ignore other opcodes
      end
    end
    block_given? ? nil : frames
  ensure
    begin
      send_close(sock)
    rescue
    end
    sock.close rescue nil
  end
end

#discover_tools!Object

Raises:



22
23
24
# File 'lib/utcp/providers/websocket_provider.rb', line 22

def discover_tools!
  raise ProviderError, "WebSocket is an execution provider only"
end