Class: SlackSocketModeBot::SimpleWebSocket

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

Instance Method Summary collapse

Constructor Details

#initialize(url) ⇒ SimpleWebSocket

Returns a new instance of SimpleWebSocket.



2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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
# File 'lib/slack_socket_mode_bot/simple_web_socket.rb', line 2

def initialize(url)
  uri = URI.parse(url)

  unless uri.scheme == "https" || uri.scheme == "wss"
    raise "unexpected scheme (not secure?): #{ uri.scheme }"
  end

  ctx = OpenSSL::SSL::SSLContext.new
  ctx.ssl_version = "SSLv23"
  ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
  ctx.cert_store = OpenSSL::X509::Store.new
  ctx.cert_store.set_default_paths

  count = 0
  begin
    io = TCPSocket.new(uri.host, uri.port || 443)
    @io = OpenSSL::SSL::SSLSocket.new(io, ctx)
    @io.connect
  rescue Socket::ResolutionError
    sleep 1
    count += 1
    retry if count < 3
    raise
  end

  @version = nil

  @fib = Fiber.new do
    closed = false
    begin
      handshake = WebSocket::Handshake::Client.new(url: url)
      @write_buff = handshake.to_s.dup
      handshake << Fiber.yield until handshake.finished?

      @version = handshake.version
      yield :open

      frame = WebSocket::Frame::Incoming::Client.new
      frame << handshake.leftovers
      while true
        while msg = frame.next
          case msg.type
          when :close
            yield :close unless closed
            closed = true
          when :ping
            send(msg.data, type: :pong)
          when :pong
          when :text
            yield :message, msg.data, :text
          when :binary
            yield :message, msg.data, :binary
          end
        end
        frame << Fiber.yield
      end
    rescue EOFError
    ensure
      yield :close unless closed
      @io.close
    end
  end

  @fib.resume
end

Instance Method Details

#close(code: 1000, reason: "") ⇒ Object



74
75
76
# File 'lib/slack_socket_mode_bot/simple_web_socket.rb', line 74

def close(code: 1000, reason: "")
  send(reason, type: :close, code: code) unless @io.closed?
end

#send(data, type: :text, code: nil) ⇒ Object



68
69
70
71
72
# File 'lib/slack_socket_mode_bot/simple_web_socket.rb', line 68

def send(data, type: :text, code: nil)
  raise "not opened yet" unless @version
  frame = WebSocket::Frame::Outgoing::Client.new(version: @version, data: data, type: type, code: code)
  @write_buff << frame.to_s
end

#step(read_ios, write_ios) ⇒ Object



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
# File 'lib/slack_socket_mode_bot/simple_web_socket.rb', line 78

def step(read_ios, write_ios)
  wait_readable = wait_writable = false

  unless @write_buff.empty?
    len = @io.write_nonblock(@write_buff, exception: false)
    case len
    when :wait_readable then wait_readable = true
    when :wait_writable then wait_writable = true
    else
      @write_buff.clear
    end
  end

  while true
    read_buff = @io.read_nonblock(4096, exception: false)
    case read_buff
    when :wait_readable then wait_readable = true; break
    when :wait_writable then wait_writable = true; break
    when nil
      raise Errno::EPIPE
    else
      @fib.resume(read_buff)
    end
  end

  read_ios << @io if wait_readable
  write_ios << @io if wait_writable
  return true

rescue Errno::EPIPE, Errno::ECONNRESET
  begin
    @fib.raise(EOFError)
  rescue FiberError
  end
  return false
end