Class: Spyder::WebSocket
- Inherits:
-
Object
- Object
- Spyder::WebSocket
- Defined in:
- lib/spyder/web_socket.rb
Defined Under Namespace
Classes: Frame
Constant Summary collapse
- WS_CONST =
'258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
Class Method Summary collapse
Instance Method Summary collapse
- #hijacked!(socket) ⇒ Object
-
#initialize ⇒ WebSocket
constructor
A new instance of WebSocket.
- #on_close(&blk) ⇒ Object
- #on_message(&blk) ⇒ Object
- #on_start(&blk) ⇒ Object
- #send_binary(data) ⇒ Object
- #send_data(data, mode) ⇒ Object
- #send_text(data) ⇒ Object
- #threaded_start! ⇒ Object
Constructor Details
#initialize ⇒ WebSocket
Returns a new instance of WebSocket.
17 18 19 20 21 22 23 24 25 26 27 28 29 |
# File 'lib/spyder/web_socket.rb', line 17 def initialize @socket = nil @on_start = nil @on_message = nil @on_close = nil @streaming_buffer = WebSocketStreamingBuffer.new do |frame, mode, fragmented, last_fragment| @on_message.call(frame, mode) unless fragmented end @streaming_buffer.on_close = proc do @socket.close rescue nil end end |
Class Method Details
.upgrade_websocket_request(request) ⇒ Object
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 |
# File 'lib/spyder/web_socket.rb', line 77 def self.upgrade_websocket_request(request) ws_key = request.headers.dict['sec-websocket-key'] return unless ws_key && request.headers.dict['upgrade'] == 'websocket' conns = request.headers.dict.fetch('connection', '').split(' ').map(&:strip) return unless conns.include?('Upgrade') ws_version = request.headers.dict['sec-websocket-version'] # expect: 13 return unless ws_version protocols = request.headers.dict['sec-websocket-protocol'] protocols = protocols.split(',').map(&:strip) if protocols extensions = request.headers.dict['sec-websocket-extensions'] extensions = extensions.split(';').map(&:strip) if extensions decoded_key = Base64.strict_decode64(ws_key) rescue nil return unless decoded_key&.length == 16 response_key = Base64.strict_encode64( OpenSSL::Digest::SHA1.digest("#{ws_key}#{WS_CONST}") ) ws = new chosen_proto = yield(ws, protocols) if (block_given? && protocols) chosen_proto = protocols.first if !chosen_proto && protocols resp = Spyder::Response.new(code: 101) resp.add_standard_headers resp.set_header 'connection', 'Upgrade' resp.set_header 'upgrade', 'websocket' resp.set_header('sec-websocket-protocol', chosen_proto) if chosen_proto resp.set_header 'sec-websocket-accept', response_key resp.hijack = proc { |client_socket| ws.hijacked!(client_socket) } [resp, ws] end |
Instance Method Details
#hijacked!(socket) ⇒ Object
69 70 71 72 73 74 75 |
# File 'lib/spyder/web_socket.rb', line 69 def hijacked!(socket) @socket = socket puts "websocket hijacked! #{socket}" @thread = Thread.start do self.threaded_start! end end |
#on_close(&blk) ⇒ Object
57 58 59 |
# File 'lib/spyder/web_socket.rb', line 57 def on_close(&blk) @on_close = blk end |
#on_message(&blk) ⇒ Object
61 62 63 |
# File 'lib/spyder/web_socket.rb', line 61 def (&blk) @on_message = blk end |
#on_start(&blk) ⇒ Object
65 66 67 |
# File 'lib/spyder/web_socket.rb', line 65 def on_start(&blk) @on_start = blk end |
#send_binary(data) ⇒ Object
35 36 37 |
# File 'lib/spyder/web_socket.rb', line 35 def send_binary(data) send_data(data, :binary) end |
#send_data(data, mode) ⇒ Object
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/spyder/web_socket.rb', line 39 def send_data(data, mode) data = data.b length = data.length buffer = String.new(encoding: 'ascii-8bit', capacity: length + 32) buffer += ((1 << 7) | (mode == :binary ? 2 : 1)).chr if length < 126 buffer += length.chr elsif length <= 0xFFFF buffer += [126, length].pack('CS>') else buffer += [127, length].pack('CQ>') end @socket.write(buffer) @socket.write(data) end |
#send_text(data) ⇒ Object
31 32 33 |
# File 'lib/spyder/web_socket.rb', line 31 def send_text(data) send_data(data, :text) end |
#threaded_start! ⇒ Object
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/spyder/web_socket.rb', line 117 def threaded_start! @on_start.call if @on_start while !@socket.closed? && !@socket.eof? data = nil begin data = @socket.read_nonblock(16 * 1024) rescue IO::WaitReadable IO.select([@socket], [], [@socket]) end next unless data puts "ws: read #{data.length} bytes" @streaming_buffer.feed(data) end @on_close.call unless @on_close end |