mirror of
https://github.com/bitwarden/help
synced 2025-12-20 10:13:15 +00:00
Promote to Master (#748)
* initial commit
* adding quotes for the array error
* Create Gemfile
* Create Gemfile.lock
* add .nvmrc and .node-version
* removed /article from URL
* update links to work with netlify
* more fixed links
* link fixes
* update bad links
* Update netlify.toml
toml test for redirects
* article redirect
* link fixes
* Update index.html
* Update netlify.toml
* Update _config.yml
* Update netlify.toml
* Update netlify.toml
* Update netlify.toml
* Update netlify.toml
* Update netlify.toml
* add article back into URL for launch
* Update netlify.toml
* Update netlify.toml
* add order to categories front matter
* Update netlify.toml
* update
* sidemenu update
* Revert "sidemenu update"
This reverts commit 5441c3d35c.
* update order prop
* Navbar updates per Gary and compiler warnings
* font/style tweaks
* Update sidebar.html
* Stage Release Documentation (#739)
* initial drafts
* rewrite Custom Fields article to prioritize new context-menu option & better organize ancillary information
* edit
* edit
* Custom Field Context Menu & CAPTCHA item in release notes
* SSO relink event
* update rn
* small edits
* improve release notes titles
* fix side menu
* Edits courtest of mportune!
* update order
* link fixes
* link cleanup
* image updates and a link
* fix trailing slash
Co-authored-by: DanHillesheim <79476558+DanHillesheim@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
63f78e8979
commit
906e2ca0dd
14
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/close03.rb
vendored
Normal file
14
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/close03.rb
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
module Close03
|
||||
def close_websocket(code, body)
|
||||
# TODO: Ideally send body data and check that it matches in ack
|
||||
send_frame(:close, '')
|
||||
@state = :closing
|
||||
start_close_timeout
|
||||
end
|
||||
|
||||
def supports_close_codes?; false; end
|
||||
end
|
||||
end
|
||||
end
|
||||
14
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/close05.rb
vendored
Normal file
14
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/close05.rb
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
module Close05
|
||||
def close_websocket(code, body)
|
||||
# TODO: Ideally send body data and check that it matches in ack
|
||||
send_frame(:close, "\x53")
|
||||
@state = :closing
|
||||
start_close_timeout
|
||||
end
|
||||
|
||||
def supports_close_codes?; false; end
|
||||
end
|
||||
end
|
||||
end
|
||||
19
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/close06.rb
vendored
Normal file
19
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/close06.rb
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
module Close06
|
||||
def close_websocket(code, body)
|
||||
if code
|
||||
close_data = [code].pack('n')
|
||||
close_data << body if body
|
||||
send_frame(:close, close_data)
|
||||
else
|
||||
send_frame(:close, '')
|
||||
end
|
||||
@state = :closing
|
||||
start_close_timeout
|
||||
end
|
||||
|
||||
def supports_close_codes?; true; end
|
||||
end
|
||||
end
|
||||
end
|
||||
11
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/close75.rb
vendored
Normal file
11
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/close75.rb
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
module Close75
|
||||
def close_websocket(code, body)
|
||||
@connection.close_connection_after_writing
|
||||
end
|
||||
|
||||
def supports_close_codes?; false; end
|
||||
end
|
||||
end
|
||||
end
|
||||
349
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/connection.rb
vendored
Normal file
349
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/connection.rb
vendored
Normal file
@@ -0,0 +1,349 @@
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
class Connection < EventMachine::Connection
|
||||
include Debugger
|
||||
|
||||
attr_writer :max_frame_size
|
||||
|
||||
# define WebSocket callbacks
|
||||
def onopen(&blk); @onopen = blk; end
|
||||
def onclose(&blk); @onclose = blk; end
|
||||
def onerror(&blk); @onerror = blk; end
|
||||
def onmessage(&blk); @onmessage = blk; end
|
||||
def onbinary(&blk); @onbinary = blk; end
|
||||
def onping(&blk); @onping = blk; end
|
||||
def onpong(&blk); @onpong = blk; end
|
||||
|
||||
def trigger_on_message(msg)
|
||||
@onmessage.call(msg) if defined? @onmessage
|
||||
end
|
||||
def trigger_on_binary(msg)
|
||||
@onbinary.call(msg) if defined? @onbinary
|
||||
end
|
||||
def trigger_on_open(handshake)
|
||||
@onopen.call(handshake) if defined? @onopen
|
||||
end
|
||||
def trigger_on_close(event = {})
|
||||
@onclose.call(event) if defined? @onclose
|
||||
end
|
||||
def trigger_on_ping(data)
|
||||
@onping.call(data) if defined? @onping
|
||||
end
|
||||
def trigger_on_pong(data)
|
||||
@onpong.call(data) if defined? @onpong
|
||||
end
|
||||
def trigger_on_error(reason)
|
||||
return false unless defined? @onerror
|
||||
@onerror.call(reason)
|
||||
true
|
||||
end
|
||||
|
||||
def initialize(options)
|
||||
@options = options
|
||||
@debug = options[:debug] || false
|
||||
@secure = options[:secure] || false
|
||||
@secure_proxy = options[:secure_proxy] || false
|
||||
@tls_options = options[:tls_options] || {}
|
||||
@close_timeout = options[:close_timeout]
|
||||
@outbound_limit = options[:outbound_limit] || 0
|
||||
|
||||
@handler = nil
|
||||
|
||||
debug [:initialize]
|
||||
end
|
||||
|
||||
# Use this method to close the websocket connection cleanly
|
||||
# This sends a close frame and waits for acknowlegement before closing
|
||||
# the connection
|
||||
def close(code = nil, body = nil)
|
||||
if code && !acceptable_close_code?(code)
|
||||
raise "Application code may only use codes from 1000, 3000-4999"
|
||||
end
|
||||
|
||||
close_websocket_private(code, body)
|
||||
end
|
||||
|
||||
# Deprecated, to be removed in version 0.6
|
||||
alias :close_websocket :close
|
||||
|
||||
def post_init
|
||||
start_tls(@tls_options) if @secure
|
||||
end
|
||||
|
||||
def receive_data(data)
|
||||
debug [:receive_data, data]
|
||||
|
||||
if @handler
|
||||
@handler.receive_data(data)
|
||||
else
|
||||
dispatch(data)
|
||||
end
|
||||
rescue => e
|
||||
debug [:error, e]
|
||||
|
||||
# There is no code defined for application errors, so use 3000
|
||||
# (which is reserved for frameworks)
|
||||
close_websocket_private(3000, "Application error")
|
||||
|
||||
# These are application errors - raise unless onerror defined
|
||||
trigger_on_error(e) || raise(e)
|
||||
end
|
||||
|
||||
def send_data(data)
|
||||
if @outbound_limit > 0 &&
|
||||
get_outbound_data_size + data.bytesize > @outbound_limit
|
||||
abort(:outbound_limit_reached)
|
||||
return 0
|
||||
end
|
||||
|
||||
super(data)
|
||||
end
|
||||
|
||||
def unbind
|
||||
debug [:unbind, :connection]
|
||||
|
||||
@handler.unbind if @handler
|
||||
rescue => e
|
||||
debug [:error, e]
|
||||
# These are application errors - raise unless onerror defined
|
||||
trigger_on_error(e) || raise(e)
|
||||
end
|
||||
|
||||
def dispatch(data)
|
||||
if data.match(%r|^GET /healthcheck|)
|
||||
send_healthcheck_response
|
||||
elsif data.match(/\A<policy-file-request\s*\/>/)
|
||||
send_flash_cross_domain_file
|
||||
else
|
||||
@handshake ||= begin
|
||||
handshake = Handshake.new(@secure || @secure_proxy)
|
||||
|
||||
handshake.callback { |upgrade_response, handler_klass|
|
||||
debug [:accepting_ws_version, handshake.protocol_version]
|
||||
debug [:upgrade_response, upgrade_response]
|
||||
self.send_data(upgrade_response)
|
||||
@handler = handler_klass.new(self, @debug)
|
||||
@handshake = nil
|
||||
trigger_on_open(handshake)
|
||||
}
|
||||
|
||||
handshake.errback { |e|
|
||||
debug [:error, e]
|
||||
trigger_on_error(e)
|
||||
# Handshake errors require the connection to be aborted
|
||||
abort(:handshake_error)
|
||||
}
|
||||
|
||||
handshake
|
||||
end
|
||||
|
||||
@handshake.receive_data(data)
|
||||
end
|
||||
end
|
||||
|
||||
def send_healthcheck_response
|
||||
debug [:healthcheck, 'OK']
|
||||
|
||||
healthcheck_res = ["HTTP/1.1 200 OK"]
|
||||
healthcheck_res << "Content-Type: text/plain"
|
||||
healthcheck_res << "Content-Length: 2"
|
||||
|
||||
healthcheck_res = healthcheck_res.join("\r\n") + "\r\n\r\nOK"
|
||||
|
||||
send_data healthcheck_res
|
||||
|
||||
# handle the healthcheck request transparently
|
||||
# no need to notify the user about this connection
|
||||
@onclose = nil
|
||||
close_connection_after_writing
|
||||
end
|
||||
|
||||
def send_flash_cross_domain_file
|
||||
file = '<?xml version="1.0"?><cross-domain-policy><allow-access-from domain="*" to-ports="*"/></cross-domain-policy>'
|
||||
debug [:cross_domain, file]
|
||||
send_data file
|
||||
|
||||
# handle the cross-domain request transparently
|
||||
# no need to notify the user about this connection
|
||||
@onclose = nil
|
||||
close_connection_after_writing
|
||||
end
|
||||
|
||||
# Cache encodings since it's moderately expensive to look them up each time
|
||||
ENCODING_SUPPORTED = "string".respond_to?(:force_encoding)
|
||||
UTF8 = Encoding.find("UTF-8") if ENCODING_SUPPORTED
|
||||
BINARY = Encoding.find("BINARY") if ENCODING_SUPPORTED
|
||||
|
||||
# Send a WebSocket text frame.
|
||||
#
|
||||
# A WebSocketError may be raised if the connection is in an opening or a
|
||||
# closing state, or if the passed in data is not valid UTF-8
|
||||
#
|
||||
def send_text(data)
|
||||
# If we're using Ruby 1.9, be pedantic about encodings
|
||||
if ENCODING_SUPPORTED
|
||||
# Also accept ascii only data in other encodings for convenience
|
||||
unless (data.encoding == UTF8 && data.valid_encoding?) || data.ascii_only?
|
||||
raise WebSocketError, "Data sent to WebSocket must be valid UTF-8 but was #{data.encoding} (valid: #{data.valid_encoding?})"
|
||||
end
|
||||
# This labels the encoding as binary so that it can be combined with
|
||||
# the BINARY framing
|
||||
data.force_encoding(BINARY)
|
||||
else
|
||||
# TODO: Check that data is valid UTF-8
|
||||
end
|
||||
|
||||
if @handler
|
||||
@handler.send_text_frame(data)
|
||||
else
|
||||
raise WebSocketError, "Cannot send data before onopen callback"
|
||||
end
|
||||
|
||||
# Revert data back to the original encoding (which we assume is UTF-8)
|
||||
# Doing this to avoid duping the string - there may be a better way
|
||||
data.force_encoding(UTF8) if ENCODING_SUPPORTED
|
||||
return nil
|
||||
end
|
||||
|
||||
alias :send :send_text
|
||||
|
||||
# Send a WebSocket binary frame.
|
||||
#
|
||||
def send_binary(data)
|
||||
if @handler
|
||||
@handler.send_frame(:binary, data)
|
||||
else
|
||||
raise WebSocketError, "Cannot send binary before onopen callback"
|
||||
end
|
||||
end
|
||||
|
||||
# Send a ping to the client. The client must respond with a pong.
|
||||
#
|
||||
# In the case that the client is running a WebSocket draft < 01, false
|
||||
# is returned since ping & pong are not supported
|
||||
#
|
||||
def ping(body = '')
|
||||
if @handler
|
||||
@handler.pingable? ? @handler.send_frame(:ping, body) && true : false
|
||||
else
|
||||
raise WebSocketError, "Cannot ping before onopen callback"
|
||||
end
|
||||
end
|
||||
|
||||
# Send an unsolicited pong message, as allowed by the protocol. The
|
||||
# client is not expected to respond to this message.
|
||||
#
|
||||
# em-websocket automatically takes care of sending pong replies to
|
||||
# incoming ping messages, as the protocol demands.
|
||||
#
|
||||
def pong(body = '')
|
||||
if @handler
|
||||
@handler.pingable? ? @handler.send_frame(:pong, body) && true : false
|
||||
else
|
||||
raise WebSocketError, "Cannot ping before onopen callback"
|
||||
end
|
||||
end
|
||||
|
||||
# Test whether the connection is pingable (i.e. the WebSocket draft in
|
||||
# use is >= 01)
|
||||
def pingable?
|
||||
if @handler
|
||||
@handler.pingable?
|
||||
else
|
||||
raise WebSocketError, "Cannot test whether pingable before onopen callback"
|
||||
end
|
||||
end
|
||||
|
||||
def supports_close_codes?
|
||||
if @handler
|
||||
@handler.supports_close_codes?
|
||||
else
|
||||
raise WebSocketError, "Cannot test before onopen callback"
|
||||
end
|
||||
end
|
||||
|
||||
def state
|
||||
@handler ? @handler.state : :handshake
|
||||
end
|
||||
|
||||
# Returns the IP address for the remote peer
|
||||
def remote_ip
|
||||
get_peername[2,6].unpack('nC4')[1..4].join('.')
|
||||
end
|
||||
|
||||
# Returns the maximum frame size which this connection is configured to
|
||||
# accept. This can be set globally or on a per connection basis, and
|
||||
# defaults to a value of 10MB if not set.
|
||||
#
|
||||
# The behaviour when a too large frame is received varies by protocol,
|
||||
# but in the newest protocols the connection will be closed with the
|
||||
# correct close code (1009) immediately after receiving the frame header
|
||||
#
|
||||
def max_frame_size
|
||||
defined?(@max_frame_size) ? @max_frame_size : WebSocket.max_frame_size
|
||||
end
|
||||
|
||||
def close_timeout
|
||||
@close_timeout || WebSocket.close_timeout
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# As definited in draft 06 7.2.2, some failures require that the server
|
||||
# abort the websocket connection rather than close cleanly
|
||||
def abort(reason)
|
||||
debug [:abort, reason]
|
||||
close_connection
|
||||
end
|
||||
|
||||
def close_websocket_private(code, body)
|
||||
if @handler
|
||||
debug [:closing, code]
|
||||
@handler.close_websocket(code, body)
|
||||
else
|
||||
# The handshake hasn't completed - should be safe to terminate
|
||||
abort(:handshake_incomplete)
|
||||
end
|
||||
end
|
||||
|
||||
# Allow applications to close with 1000, 1003, 1008, 1011, 3xxx or 4xxx.
|
||||
#
|
||||
# em-websocket uses a few other codes internally which should not be
|
||||
# used by applications
|
||||
#
|
||||
# Browsers generally allow connections to be closed with code 1000,
|
||||
# 3xxx, and 4xxx. em-websocket allows closing with a few other codes
|
||||
# which seem reasonable (for discussion see
|
||||
# https://github.com/igrigorik/em-websocket/issues/98)
|
||||
#
|
||||
# Usage from the rfc:
|
||||
#
|
||||
# 1000 indicates a normal closure
|
||||
#
|
||||
# 1003 indicates that an endpoint is terminating the connection
|
||||
# because it has received a type of data it cannot accept
|
||||
#
|
||||
# 1008 indicates that an endpoint is terminating the connection because
|
||||
# it has received a message that violates its policy
|
||||
#
|
||||
# 1011 indicates that a server is terminating the connection because it
|
||||
# encountered an unexpected condition that prevented it from fulfilling
|
||||
# the request
|
||||
#
|
||||
# Status codes in the range 3000-3999 are reserved for use by libraries,
|
||||
# frameworks, and applications
|
||||
#
|
||||
# Status codes in the range 4000-4999 are reserved for private use and
|
||||
# thus can't be registered
|
||||
#
|
||||
def acceptable_close_code?(code)
|
||||
case code
|
||||
when 1000, 1003, 1008, 1011, (3000..4999)
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
17
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/debugger.rb
vendored
Normal file
17
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/debugger.rb
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
module Debugger
|
||||
|
||||
private
|
||||
|
||||
def debug(*data)
|
||||
if @debug
|
||||
require 'pp'
|
||||
pp data
|
||||
puts
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
162
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/framing03.rb
vendored
Normal file
162
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/framing03.rb
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
# encoding: BINARY
|
||||
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
module Framing03
|
||||
def initialize_framing
|
||||
@data = ''
|
||||
@application_data_buffer = '' # Used for MORE frames
|
||||
@frame_type = nil
|
||||
end
|
||||
|
||||
def process_data
|
||||
error = false
|
||||
|
||||
while !error && @data.size > 1
|
||||
pointer = 0
|
||||
|
||||
more = ((@data.getbyte(pointer) & 0b10000000) == 0b10000000) ^ fin
|
||||
# Ignoring rsv1-3 for now
|
||||
opcode = @data.getbyte(0) & 0b00001111
|
||||
pointer += 1
|
||||
|
||||
# Ignoring rsv4
|
||||
length = @data.getbyte(pointer) & 0b01111111
|
||||
pointer += 1
|
||||
|
||||
payload_length = case length
|
||||
when 127 # Length defined by 8 bytes
|
||||
# Check buffer size
|
||||
if @data.getbyte(pointer+8-1) == nil
|
||||
debug [:buffer_incomplete, @data]
|
||||
error = true
|
||||
next
|
||||
end
|
||||
|
||||
# Only using the last 4 bytes for now, till I work out how to
|
||||
# unpack 8 bytes. I'm sure 4GB frames will do for now :)
|
||||
l = @data[(pointer+4)..(pointer+7)].unpack('N').first
|
||||
pointer += 8
|
||||
l
|
||||
when 126 # Length defined by 2 bytes
|
||||
# Check buffer size
|
||||
if @data.getbyte(pointer+2-1) == nil
|
||||
debug [:buffer_incomplete, @data]
|
||||
error = true
|
||||
next
|
||||
end
|
||||
|
||||
l = @data[pointer..(pointer+1)].unpack('n').first
|
||||
pointer += 2
|
||||
l
|
||||
else
|
||||
length
|
||||
end
|
||||
|
||||
if payload_length > @connection.max_frame_size
|
||||
raise WSMessageTooBigError, "Frame length too long (#{payload_length} bytes)"
|
||||
end
|
||||
|
||||
# Check buffer size
|
||||
if @data.getbyte(pointer+payload_length-1) == nil
|
||||
debug [:buffer_incomplete, @data]
|
||||
error = true
|
||||
next
|
||||
end
|
||||
|
||||
# Throw away data up to pointer
|
||||
@data.slice!(0...pointer)
|
||||
|
||||
# Read application data
|
||||
application_data = @data.slice!(0...payload_length)
|
||||
|
||||
frame_type = opcode_to_type(opcode)
|
||||
|
||||
if frame_type == :continuation && !@frame_type
|
||||
raise WSProtocolError, 'Continuation frame not expected'
|
||||
end
|
||||
|
||||
if more
|
||||
debug [:moreframe, frame_type, application_data]
|
||||
@application_data_buffer << application_data
|
||||
# The message type is passed in the first frame
|
||||
@frame_type ||= frame_type
|
||||
else
|
||||
# Message is complete
|
||||
if frame_type == :continuation
|
||||
@application_data_buffer << application_data
|
||||
message(@frame_type, '', @application_data_buffer)
|
||||
@application_data_buffer = ''
|
||||
@frame_type = nil
|
||||
else
|
||||
message(frame_type, '', application_data)
|
||||
end
|
||||
end
|
||||
end # end while
|
||||
end
|
||||
|
||||
def send_frame(frame_type, application_data)
|
||||
debug [:sending_frame, frame_type, application_data]
|
||||
|
||||
if @state == :closing && data_frame?(frame_type)
|
||||
raise WebSocketError, "Cannot send data frame since connection is closing"
|
||||
end
|
||||
|
||||
frame = ''
|
||||
|
||||
opcode = type_to_opcode(frame_type)
|
||||
byte1 = opcode # since more, rsv1-3 are 0
|
||||
frame << byte1
|
||||
|
||||
length = application_data.size
|
||||
if length <= 125
|
||||
byte2 = length # since rsv4 is 0
|
||||
frame << byte2
|
||||
elsif length < 65536 # write 2 byte length
|
||||
frame << 126
|
||||
frame << [length].pack('n')
|
||||
else # write 8 byte length
|
||||
frame << 127
|
||||
frame << [length >> 32, length & 0xFFFFFFFF].pack("NN")
|
||||
end
|
||||
|
||||
frame << application_data
|
||||
|
||||
@connection.send_data(frame)
|
||||
end
|
||||
|
||||
def send_text_frame(data)
|
||||
send_frame(:text, data)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# This allows flipping the more bit to fin for draft 04
|
||||
def fin; false; end
|
||||
|
||||
FRAME_TYPES = {
|
||||
:continuation => 0,
|
||||
:close => 1,
|
||||
:ping => 2,
|
||||
:pong => 3,
|
||||
:text => 4,
|
||||
:binary => 5
|
||||
}
|
||||
FRAME_TYPES_INVERSE = FRAME_TYPES.invert
|
||||
# Frames are either data frames or control frames
|
||||
DATA_FRAMES = [:text, :binary, :continuation]
|
||||
|
||||
def type_to_opcode(frame_type)
|
||||
FRAME_TYPES[frame_type] || raise("Unknown frame type")
|
||||
end
|
||||
|
||||
def opcode_to_type(opcode)
|
||||
FRAME_TYPES_INVERSE[opcode] || raise(WSProtocolError, "Unknown opcode #{opcode}")
|
||||
end
|
||||
|
||||
def data_frame?(type)
|
||||
DATA_FRAMES.include?(type)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
15
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/framing04.rb
vendored
Normal file
15
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/framing04.rb
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# encoding: BINARY
|
||||
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
# The only difference between draft 03 framing and draft 04 framing is
|
||||
# that the MORE bit has been changed to a FIN bit
|
||||
module Framing04
|
||||
include Framing03
|
||||
|
||||
private
|
||||
|
||||
def fin; true; end
|
||||
end
|
||||
end
|
||||
end
|
||||
163
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/framing05.rb
vendored
Normal file
163
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/framing05.rb
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
# encoding: BINARY
|
||||
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
module Framing05
|
||||
def initialize_framing
|
||||
@data = MaskedString.new
|
||||
@application_data_buffer = '' # Used for MORE frames
|
||||
@frame_type = nil
|
||||
end
|
||||
|
||||
def process_data
|
||||
error = false
|
||||
|
||||
while !error && @data.size > 5 # mask plus first byte present
|
||||
pointer = 0
|
||||
|
||||
@data.read_mask
|
||||
pointer += 4
|
||||
|
||||
fin = (@data.getbyte(pointer) & 0b10000000) == 0b10000000
|
||||
# Ignoring rsv1-3 for now
|
||||
opcode = @data.getbyte(pointer) & 0b00001111
|
||||
pointer += 1
|
||||
|
||||
# Ignoring rsv4
|
||||
length = @data.getbyte(pointer) & 0b01111111
|
||||
pointer += 1
|
||||
|
||||
payload_length = case length
|
||||
when 127 # Length defined by 8 bytes
|
||||
# Check buffer size
|
||||
if @data.getbyte(pointer+8-1) == nil
|
||||
debug [:buffer_incomplete, @data]
|
||||
error = true
|
||||
next
|
||||
end
|
||||
|
||||
# Only using the last 4 bytes for now, till I work out how to
|
||||
# unpack 8 bytes. I'm sure 4GB frames will do for now :)
|
||||
l = @data.getbytes(pointer+4, 4).unpack('N').first
|
||||
pointer += 8
|
||||
l
|
||||
when 126 # Length defined by 2 bytes
|
||||
# Check buffer size
|
||||
if @data.getbyte(pointer+2-1) == nil
|
||||
debug [:buffer_incomplete, @data]
|
||||
error = true
|
||||
next
|
||||
end
|
||||
|
||||
l = @data.getbytes(pointer, 2).unpack('n').first
|
||||
pointer += 2
|
||||
l
|
||||
else
|
||||
length
|
||||
end
|
||||
|
||||
if payload_length > @connection.max_frame_size
|
||||
raise WSMessageTooBigError, "Frame length too long (#{payload_length} bytes)"
|
||||
end
|
||||
|
||||
# Check buffer size
|
||||
if @data.getbyte(pointer+payload_length-1) == nil
|
||||
debug [:buffer_incomplete, @data]
|
||||
error = true
|
||||
next
|
||||
end
|
||||
|
||||
# Read application data
|
||||
application_data = @data.getbytes(pointer, payload_length)
|
||||
pointer += payload_length
|
||||
|
||||
# Throw away data up to pointer
|
||||
@data.unset_mask
|
||||
@data.slice!(0...pointer)
|
||||
|
||||
frame_type = opcode_to_type(opcode)
|
||||
|
||||
if frame_type == :continuation && !@frame_type
|
||||
raise WSProtocolError, 'Continuation frame not expected'
|
||||
end
|
||||
|
||||
if !fin
|
||||
debug [:moreframe, frame_type, application_data]
|
||||
@application_data_buffer << application_data
|
||||
@frame_type = frame_type
|
||||
else
|
||||
# Message is complete
|
||||
if frame_type == :continuation
|
||||
@application_data_buffer << application_data
|
||||
message(@frame_type, '', @application_data_buffer)
|
||||
@application_data_buffer = ''
|
||||
@frame_type = nil
|
||||
else
|
||||
message(frame_type, '', application_data)
|
||||
end
|
||||
end
|
||||
end # end while
|
||||
end
|
||||
|
||||
def send_frame(frame_type, application_data)
|
||||
debug [:sending_frame, frame_type, application_data]
|
||||
|
||||
if @state == :closing && data_frame?(frame_type)
|
||||
raise WebSocketError, "Cannot send data frame since connection is closing"
|
||||
end
|
||||
|
||||
frame = ''
|
||||
|
||||
opcode = type_to_opcode(frame_type)
|
||||
byte1 = opcode | 0b10000000 # fin bit set, rsv1-3 are 0
|
||||
frame << byte1
|
||||
|
||||
length = application_data.size
|
||||
if length <= 125
|
||||
byte2 = length # since rsv4 is 0
|
||||
frame << byte2
|
||||
elsif length < 65536 # write 2 byte length
|
||||
frame << 126
|
||||
frame << [length].pack('n')
|
||||
else # write 8 byte length
|
||||
frame << 127
|
||||
frame << [length >> 32, length & 0xFFFFFFFF].pack("NN")
|
||||
end
|
||||
|
||||
frame << application_data
|
||||
|
||||
@connection.send_data(frame)
|
||||
end
|
||||
|
||||
def send_text_frame(data)
|
||||
send_frame(:text, data)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
FRAME_TYPES = {
|
||||
:continuation => 0,
|
||||
:close => 1,
|
||||
:ping => 2,
|
||||
:pong => 3,
|
||||
:text => 4,
|
||||
:binary => 5
|
||||
}
|
||||
FRAME_TYPES_INVERSE = FRAME_TYPES.invert
|
||||
# Frames are either data frames or control frames
|
||||
DATA_FRAMES = [:text, :binary, :continuation]
|
||||
|
||||
def type_to_opcode(frame_type)
|
||||
FRAME_TYPES[frame_type] || raise("Unknown frame type")
|
||||
end
|
||||
|
||||
def opcode_to_type(opcode)
|
||||
FRAME_TYPES_INVERSE[opcode] || raise(WSProtocolError, "Unknown opcode #{opcode}")
|
||||
end
|
||||
|
||||
def data_frame?(type)
|
||||
DATA_FRAMES.include?(type)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
185
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/framing07.rb
vendored
Normal file
185
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/framing07.rb
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
# encoding: BINARY
|
||||
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
module Framing07
|
||||
|
||||
def initialize_framing
|
||||
@data = MaskedString.new
|
||||
@application_data_buffer = '' # Used for MORE frames
|
||||
@frame_type = nil
|
||||
end
|
||||
|
||||
def process_data
|
||||
error = false
|
||||
|
||||
while !error && @data.size >= 2
|
||||
pointer = 0
|
||||
|
||||
fin = (@data.getbyte(pointer) & 0b10000000) == 0b10000000
|
||||
# Ignoring rsv1-3 for now
|
||||
opcode = @data.getbyte(pointer) & 0b00001111
|
||||
pointer += 1
|
||||
|
||||
mask = (@data.getbyte(pointer) & 0b10000000) == 0b10000000
|
||||
length = @data.getbyte(pointer) & 0b01111111
|
||||
pointer += 1
|
||||
|
||||
# raise WebSocketError, 'Data from client must be masked' unless mask
|
||||
|
||||
payload_length = case length
|
||||
when 127 # Length defined by 8 bytes
|
||||
# Check buffer size
|
||||
if @data.getbyte(pointer+8-1) == nil
|
||||
debug [:buffer_incomplete, @data]
|
||||
error = true
|
||||
next
|
||||
end
|
||||
|
||||
# Only using the last 4 bytes for now, till I work out how to
|
||||
# unpack 8 bytes. I'm sure 4GB frames will do for now :)
|
||||
l = @data.getbytes(pointer+4, 4).unpack('N').first
|
||||
pointer += 8
|
||||
l
|
||||
when 126 # Length defined by 2 bytes
|
||||
# Check buffer size
|
||||
if @data.getbyte(pointer+2-1) == nil
|
||||
debug [:buffer_incomplete, @data]
|
||||
error = true
|
||||
next
|
||||
end
|
||||
|
||||
l = @data.getbytes(pointer, 2).unpack('n').first
|
||||
pointer += 2
|
||||
l
|
||||
else
|
||||
length
|
||||
end
|
||||
|
||||
# Compute the expected frame length
|
||||
frame_length = pointer + payload_length
|
||||
frame_length += 4 if mask
|
||||
|
||||
if frame_length > @connection.max_frame_size
|
||||
raise WSMessageTooBigError, "Frame length too long (#{frame_length} bytes)"
|
||||
end
|
||||
|
||||
# Check buffer size
|
||||
if @data.getbyte(frame_length - 1) == nil
|
||||
debug [:buffer_incomplete, @data]
|
||||
error = true
|
||||
next
|
||||
end
|
||||
|
||||
# Remove frame header
|
||||
@data.slice!(0...pointer)
|
||||
pointer = 0
|
||||
|
||||
# Read application data (unmasked if required)
|
||||
@data.read_mask if mask
|
||||
pointer += 4 if mask
|
||||
application_data = @data.getbytes(pointer, payload_length)
|
||||
pointer += payload_length
|
||||
@data.unset_mask if mask
|
||||
|
||||
# Throw away data up to pointer
|
||||
@data.slice!(0...pointer)
|
||||
|
||||
frame_type = opcode_to_type(opcode)
|
||||
|
||||
if frame_type == :continuation
|
||||
if !@frame_type
|
||||
raise WSProtocolError, 'Continuation frame not expected'
|
||||
end
|
||||
else # Not a continuation frame
|
||||
if @frame_type && data_frame?(frame_type)
|
||||
raise WSProtocolError, "Continuation frame expected"
|
||||
end
|
||||
end
|
||||
|
||||
# Validate that control frames are not fragmented
|
||||
if !fin && !data_frame?(frame_type)
|
||||
raise WSProtocolError, 'Control frames must not be fragmented'
|
||||
end
|
||||
|
||||
if !fin
|
||||
debug [:moreframe, frame_type, application_data]
|
||||
@application_data_buffer << application_data
|
||||
# The message type is passed in the first frame
|
||||
@frame_type ||= frame_type
|
||||
else
|
||||
# Message is complete
|
||||
if frame_type == :continuation
|
||||
@application_data_buffer << application_data
|
||||
message(@frame_type, '', @application_data_buffer)
|
||||
@application_data_buffer = ''
|
||||
@frame_type = nil
|
||||
else
|
||||
message(frame_type, '', application_data)
|
||||
end
|
||||
end
|
||||
end # end while
|
||||
end
|
||||
|
||||
def send_frame(frame_type, application_data)
|
||||
debug [:sending_frame, frame_type, application_data]
|
||||
|
||||
if @state == :closing && data_frame?(frame_type)
|
||||
raise WebSocketError, "Cannot send data frame since connection is closing"
|
||||
end
|
||||
|
||||
frame = ''
|
||||
|
||||
opcode = type_to_opcode(frame_type)
|
||||
byte1 = opcode | 0b10000000 # fin bit set, rsv1-3 are 0
|
||||
frame << byte1
|
||||
|
||||
length = application_data.size
|
||||
if length <= 125
|
||||
byte2 = length # since rsv4 is 0
|
||||
frame << byte2
|
||||
elsif length < 65536 # write 2 byte length
|
||||
frame << 126
|
||||
frame << [length].pack('n')
|
||||
else # write 8 byte length
|
||||
frame << 127
|
||||
frame << [length >> 32, length & 0xFFFFFFFF].pack("NN")
|
||||
end
|
||||
|
||||
frame << application_data
|
||||
|
||||
@connection.send_data(frame)
|
||||
end
|
||||
|
||||
def send_text_frame(data)
|
||||
send_frame(:text, data)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
FRAME_TYPES = {
|
||||
:continuation => 0,
|
||||
:text => 1,
|
||||
:binary => 2,
|
||||
:close => 8,
|
||||
:ping => 9,
|
||||
:pong => 10,
|
||||
}
|
||||
FRAME_TYPES_INVERSE = FRAME_TYPES.invert
|
||||
# Frames are either data frames or control frames
|
||||
DATA_FRAMES = [:text, :binary, :continuation]
|
||||
|
||||
def type_to_opcode(frame_type)
|
||||
FRAME_TYPES[frame_type] || raise("Unknown frame type")
|
||||
end
|
||||
|
||||
def opcode_to_type(opcode)
|
||||
FRAME_TYPES_INVERSE[opcode] || raise(WSProtocolError, "Unknown opcode #{opcode}")
|
||||
end
|
||||
|
||||
def data_frame?(type)
|
||||
DATA_FRAMES.include?(type)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
105
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/framing76.rb
vendored
Normal file
105
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/framing76.rb
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
# encoding: BINARY
|
||||
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
module Framing76
|
||||
def initialize_framing
|
||||
@data = ''
|
||||
end
|
||||
|
||||
def process_data
|
||||
debug [:message, @data]
|
||||
|
||||
# This algorithm comes straight from the spec
|
||||
# http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#section-5.3
|
||||
|
||||
error = false
|
||||
|
||||
while !error
|
||||
return if @data.size == 0
|
||||
|
||||
pointer = 0
|
||||
frame_type = @data.getbyte(pointer)
|
||||
pointer += 1
|
||||
|
||||
if (frame_type & 0x80) == 0x80
|
||||
# If the high-order bit of the /frame type/ byte is set
|
||||
length = 0
|
||||
|
||||
loop do
|
||||
return false if !@data.getbyte(pointer)
|
||||
b = @data.getbyte(pointer)
|
||||
pointer += 1
|
||||
b_v = b & 0x7F
|
||||
length = length * 128 + b_v
|
||||
break unless (b & 0x80) == 0x80
|
||||
end
|
||||
|
||||
if length > @connection.max_frame_size
|
||||
raise WSMessageTooBigError, "Frame length too long (#{length} bytes)"
|
||||
end
|
||||
|
||||
if @data.getbyte(pointer+length-1) == nil
|
||||
debug [:buffer_incomplete, @data]
|
||||
# Incomplete data - leave @data to accumulate
|
||||
error = true
|
||||
else
|
||||
# Straight from spec - I'm sure this isn't crazy...
|
||||
# 6. Read /length/ bytes.
|
||||
# 7. Discard the read bytes.
|
||||
@data = @data[(pointer+length)..-1]
|
||||
|
||||
# If the /frame type/ is 0xFF and the /length/ was 0, then close
|
||||
if length == 0
|
||||
@connection.send_data("\xff\x00")
|
||||
@state = :closing
|
||||
@connection.close_connection_after_writing
|
||||
else
|
||||
error = true
|
||||
end
|
||||
end
|
||||
else
|
||||
# If the high-order bit of the /frame type/ byte is _not_ set
|
||||
|
||||
if @data.getbyte(0) != 0x00
|
||||
# Close the connection since this buffer can never match
|
||||
raise WSProtocolError, "Invalid frame received"
|
||||
end
|
||||
|
||||
# Addition to the spec to protect against malicious requests
|
||||
if @data.size > @connection.max_frame_size
|
||||
raise WSMessageTooBigError, "Frame length too long (#{@data.size} bytes)"
|
||||
end
|
||||
|
||||
msg = @data.slice!(/\A\x00[^\xff]*\xff/)
|
||||
if msg
|
||||
msg.gsub!(/\A\x00|\xff\z/, '')
|
||||
if @state == :closing
|
||||
debug [:ignored_message, msg]
|
||||
else
|
||||
msg.force_encoding('UTF-8') if msg.respond_to?(:force_encoding)
|
||||
@connection.trigger_on_message(msg)
|
||||
end
|
||||
else
|
||||
error = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
# frames need to start with 0x00-0x7f byte and end with
|
||||
# an 0xFF byte. Per spec, we can also set the first
|
||||
# byte to a value betweent 0x80 and 0xFF, followed by
|
||||
# a leading length indicator
|
||||
def send_text_frame(data)
|
||||
debug [:sending_text_frame, data]
|
||||
ary = ["\x00", data, "\xff"]
|
||||
ary.collect{ |s| s.force_encoding('UTF-8') if s.respond_to?(:force_encoding) }
|
||||
@connection.send_data(ary.join)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
97
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handler.rb
vendored
Normal file
97
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handler.rb
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
class Handler
|
||||
def self.klass_factory(version)
|
||||
case version
|
||||
when 75
|
||||
Handler75
|
||||
when 76
|
||||
Handler76
|
||||
when 1..3
|
||||
# We'll use handler03 - I believe they're all compatible
|
||||
Handler03
|
||||
when 5
|
||||
Handler05
|
||||
when 6
|
||||
Handler06
|
||||
when 7
|
||||
Handler07
|
||||
when 8
|
||||
# drafts 9, 10, 11 and 12 should never change the version
|
||||
# number as they are all the same as version 08.
|
||||
Handler08
|
||||
when 13
|
||||
# drafts 13 to 17 all identify as version 13 as they are
|
||||
# only minor changes or text changes.
|
||||
Handler13
|
||||
else
|
||||
# According to spec should abort the connection
|
||||
raise HandshakeError, "Protocol version #{version} not supported"
|
||||
end
|
||||
end
|
||||
|
||||
include Debugger
|
||||
|
||||
attr_reader :request, :state
|
||||
|
||||
def initialize(connection, debug = false)
|
||||
@connection = connection
|
||||
@debug = debug
|
||||
@state = :connected
|
||||
@close_timer = nil
|
||||
initialize_framing
|
||||
end
|
||||
|
||||
def receive_data(data)
|
||||
@data << data
|
||||
process_data
|
||||
rescue WSProtocolError => e
|
||||
fail_websocket(e)
|
||||
end
|
||||
|
||||
def close_websocket(code, body)
|
||||
# Implemented in subclass
|
||||
end
|
||||
|
||||
# Used to avoid un-acked and unclosed remaining open indefinitely
|
||||
def start_close_timeout
|
||||
@close_timer = EM::Timer.new(@connection.close_timeout) {
|
||||
@connection.close_connection
|
||||
e = WSProtocolError.new("Close handshake un-acked after #{@connection.close_timeout}s, closing tcp connection")
|
||||
@connection.trigger_on_error(e)
|
||||
}
|
||||
end
|
||||
|
||||
# This corresponds to "Fail the WebSocket Connection" in the spec.
|
||||
def fail_websocket(e)
|
||||
debug [:error, e]
|
||||
close_websocket(e.code, e.message)
|
||||
@connection.close_connection_after_writing
|
||||
@connection.trigger_on_error(e)
|
||||
end
|
||||
|
||||
def unbind
|
||||
@state = :closed
|
||||
|
||||
@close_timer.cancel if @close_timer
|
||||
|
||||
@close_info = defined?(@close_info) ? @close_info : {
|
||||
:code => 1006,
|
||||
:was_clean => false,
|
||||
}
|
||||
|
||||
@connection.trigger_on_close(@close_info)
|
||||
end
|
||||
|
||||
def ping
|
||||
# Overridden in subclass
|
||||
false
|
||||
end
|
||||
|
||||
def pingable?
|
||||
# Also Overridden
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
9
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handler03.rb
vendored
Normal file
9
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handler03.rb
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
class Handler03 < Handler
|
||||
include Framing03
|
||||
include MessageProcessor03
|
||||
include Close03
|
||||
end
|
||||
end
|
||||
end
|
||||
9
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handler05.rb
vendored
Normal file
9
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handler05.rb
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
class Handler05 < Handler
|
||||
include Framing05
|
||||
include MessageProcessor03
|
||||
include Close05
|
||||
end
|
||||
end
|
||||
end
|
||||
9
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handler06.rb
vendored
Normal file
9
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handler06.rb
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
class Handler06 < Handler
|
||||
include Framing05
|
||||
include MessageProcessor06
|
||||
include Close06
|
||||
end
|
||||
end
|
||||
end
|
||||
9
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handler07.rb
vendored
Normal file
9
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handler07.rb
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
class Handler07 < Handler
|
||||
include Framing07
|
||||
include MessageProcessor06
|
||||
include Close06
|
||||
end
|
||||
end
|
||||
end
|
||||
9
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handler08.rb
vendored
Normal file
9
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handler08.rb
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
class Handler08 < Handler
|
||||
include Framing07
|
||||
include MessageProcessor06
|
||||
include Close06
|
||||
end
|
||||
end
|
||||
end
|
||||
9
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handler13.rb
vendored
Normal file
9
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handler13.rb
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
class Handler13 < Handler
|
||||
include Framing07
|
||||
include MessageProcessor06
|
||||
include Close06
|
||||
end
|
||||
end
|
||||
end
|
||||
9
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handler75.rb
vendored
Normal file
9
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handler75.rb
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
class Handler75 < Handler
|
||||
include Handshake75
|
||||
include Framing76
|
||||
include Close75
|
||||
end
|
||||
end
|
||||
end
|
||||
14
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handler76.rb
vendored
Normal file
14
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handler76.rb
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
# encoding: BINARY
|
||||
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
class Handler76 < Handler
|
||||
include Handshake76
|
||||
include Framing76
|
||||
include Close75
|
||||
|
||||
# "\377\000" is octet version and "\xff\x00" is hex version
|
||||
TERMINATE_STRING = "\xff\x00"
|
||||
end
|
||||
end
|
||||
end
|
||||
156
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handshake.rb
vendored
Normal file
156
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handshake.rb
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
require "http/parser"
|
||||
require "uri"
|
||||
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
|
||||
# Resposible for creating the server handshake response
|
||||
class Handshake
|
||||
include EM::Deferrable
|
||||
|
||||
attr_reader :parser, :protocol_version
|
||||
|
||||
# Unfortunately drafts 75 & 76 require knowledge of whether the
|
||||
# connection is being terminated as ws/wss in order to generate the
|
||||
# correct handshake response
|
||||
def initialize(secure)
|
||||
@parser = Http::Parser.new
|
||||
@secure = secure
|
||||
|
||||
@parser.on_headers_complete = proc { |headers|
|
||||
@headers = Hash[headers.map { |k,v| [k.downcase, v] }]
|
||||
}
|
||||
end
|
||||
|
||||
def receive_data(data)
|
||||
@parser << data
|
||||
|
||||
if defined? @headers
|
||||
process(@headers, @parser.upgrade_data)
|
||||
end
|
||||
rescue HTTP::Parser::Error => e
|
||||
fail(HandshakeError.new("Invalid HTTP header: #{e.message}"))
|
||||
end
|
||||
|
||||
# Returns the WebSocket upgrade headers as a hash.
|
||||
#
|
||||
# Keys are strings, unmodified from the request.
|
||||
#
|
||||
def headers
|
||||
@parser.headers
|
||||
end
|
||||
|
||||
# The same as headers, except that the hash keys are downcased
|
||||
#
|
||||
def headers_downcased
|
||||
@headers
|
||||
end
|
||||
|
||||
# Returns the request path (excluding any query params)
|
||||
#
|
||||
def path
|
||||
@path
|
||||
end
|
||||
|
||||
# Returns the query params as a string foo=bar&baz=...
|
||||
def query_string
|
||||
@query_string
|
||||
end
|
||||
|
||||
def query
|
||||
Hash[query_string.split('&').map { |c| c.split('=', 2) }]
|
||||
end
|
||||
|
||||
# Returns the WebSocket origin header if provided
|
||||
#
|
||||
def origin
|
||||
@headers["origin"] || @headers["sec-websocket-origin"] || nil
|
||||
end
|
||||
|
||||
def secure?
|
||||
@secure
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def process(headers, remains)
|
||||
unless @parser.http_method == "GET"
|
||||
raise HandshakeError, "Must be GET request"
|
||||
end
|
||||
|
||||
# Validate request path
|
||||
#
|
||||
# According to http://tools.ietf.org/search/rfc2616#section-5.1.2, an
|
||||
# invalid Request-URI should result in a 400 status code, but
|
||||
# HandshakeError's currently result in a WebSocket abort. It's not
|
||||
# clear which should take precedence, but an abort will do just fine.
|
||||
begin
|
||||
uri = URI.parse(@parser.request_url)
|
||||
@path = uri.path
|
||||
@query_string = uri.query || ""
|
||||
rescue URI::InvalidURIError
|
||||
raise HandshakeError, "Invalid request URI: #{@parser.request_url}"
|
||||
end
|
||||
|
||||
# Validate Upgrade
|
||||
unless @parser.upgrade?
|
||||
raise HandshakeError, "Not an upgrade request"
|
||||
end
|
||||
upgrade = @headers['upgrade']
|
||||
unless upgrade.kind_of?(String) && upgrade.downcase == 'websocket'
|
||||
raise HandshakeError, "Invalid upgrade header: #{upgrade.inspect}"
|
||||
end
|
||||
|
||||
# Determine version heuristically
|
||||
version = if @headers['sec-websocket-version']
|
||||
# Used from drafts 04 onwards
|
||||
@headers['sec-websocket-version'].to_i
|
||||
elsif @headers['sec-websocket-draft']
|
||||
# Used in drafts 01 - 03
|
||||
@headers['sec-websocket-draft'].to_i
|
||||
elsif @headers['sec-websocket-key1']
|
||||
76
|
||||
else
|
||||
75
|
||||
end
|
||||
|
||||
# Additional handling of bytes after the header if required
|
||||
case version
|
||||
when 75
|
||||
if !remains.empty?
|
||||
raise HandshakeError, "Extra bytes after header"
|
||||
end
|
||||
when 76, 1..3
|
||||
if remains.length < 8
|
||||
# The whole third-key has not been received yet.
|
||||
return nil
|
||||
elsif remains.length > 8
|
||||
raise HandshakeError, "Extra bytes after third key"
|
||||
end
|
||||
@headers['third-key'] = remains
|
||||
end
|
||||
|
||||
handshake_klass = case version
|
||||
when 75
|
||||
Handshake75
|
||||
when 76, 1..3
|
||||
Handshake76
|
||||
when 5, 6, 7, 8, 13
|
||||
Handshake04
|
||||
else
|
||||
# According to spec should abort the connection
|
||||
raise HandshakeError, "Protocol version #{version} not supported"
|
||||
end
|
||||
|
||||
upgrade_response = handshake_klass.handshake(@headers, @parser.request_url, @secure)
|
||||
|
||||
handler_klass = Handler.klass_factory(version)
|
||||
|
||||
@protocol_version = version
|
||||
succeed(upgrade_response, handler_klass)
|
||||
rescue HandshakeError => e
|
||||
fail(e)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
37
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handshake04.rb
vendored
Normal file
37
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handshake04.rb
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
require 'digest/sha1'
|
||||
require 'base64'
|
||||
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
module Handshake04
|
||||
def self.handshake(headers, _, __)
|
||||
# Required
|
||||
unless key = headers['sec-websocket-key']
|
||||
raise HandshakeError, "sec-websocket-key header is required"
|
||||
end
|
||||
|
||||
string_to_sign = "#{key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
||||
signature = Base64.encode64(Digest::SHA1.digest(string_to_sign)).chomp
|
||||
|
||||
upgrade = ["HTTP/1.1 101 Switching Protocols"]
|
||||
upgrade << "Upgrade: websocket"
|
||||
upgrade << "Connection: Upgrade"
|
||||
upgrade << "Sec-WebSocket-Accept: #{signature}"
|
||||
if protocol = headers['sec-websocket-protocol']
|
||||
validate_protocol!(protocol)
|
||||
upgrade << "Sec-WebSocket-Protocol: #{protocol}"
|
||||
end
|
||||
|
||||
# TODO: Support sec-websocket-protocol selection
|
||||
# TODO: sec-websocket-extensions
|
||||
|
||||
return upgrade.join("\r\n") + "\r\n\r\n"
|
||||
end
|
||||
|
||||
def self.validate_protocol!(protocol)
|
||||
raise HandshakeError, "Invalid WebSocket-Protocol: empty" if protocol.empty?
|
||||
# TODO: Validate characters
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
28
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handshake75.rb
vendored
Normal file
28
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handshake75.rb
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
module Handshake75
|
||||
def self.handshake(headers, path, secure)
|
||||
scheme = (secure ? "wss" : "ws")
|
||||
location = "#{scheme}://#{headers['host']}#{path}"
|
||||
|
||||
upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
|
||||
upgrade << "Upgrade: WebSocket\r\n"
|
||||
upgrade << "Connection: Upgrade\r\n"
|
||||
upgrade << "WebSocket-Origin: #{headers['origin']}\r\n"
|
||||
upgrade << "WebSocket-Location: #{location}\r\n"
|
||||
if protocol = headers['sec-websocket-protocol']
|
||||
validate_protocol!(protocol)
|
||||
upgrade << "Sec-WebSocket-Protocol: #{protocol}\r\n"
|
||||
end
|
||||
upgrade << "\r\n"
|
||||
|
||||
return upgrade
|
||||
end
|
||||
|
||||
def self.validate_protocol!(protocol)
|
||||
raise HandshakeError, "Invalid WebSocket-Protocol: empty" if protocol.empty?
|
||||
# TODO: Validate characters
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
72
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handshake76.rb
vendored
Normal file
72
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/handshake76.rb
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
require 'digest/md5'
|
||||
|
||||
module EventMachine::WebSocket
|
||||
module Handshake76
|
||||
class << self
|
||||
def handshake(headers, path, secure)
|
||||
challenge_response = solve_challenge(
|
||||
headers['sec-websocket-key1'],
|
||||
headers['sec-websocket-key2'],
|
||||
headers['third-key']
|
||||
)
|
||||
|
||||
scheme = (secure ? "wss" : "ws")
|
||||
location = "#{scheme}://#{headers['host']}#{path}"
|
||||
|
||||
upgrade = "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
|
||||
upgrade << "Upgrade: WebSocket\r\n"
|
||||
upgrade << "Connection: Upgrade\r\n"
|
||||
upgrade << "Sec-WebSocket-Location: #{location}\r\n"
|
||||
upgrade << "Sec-WebSocket-Origin: #{headers['origin']}\r\n"
|
||||
if protocol = headers['sec-websocket-protocol']
|
||||
validate_protocol!(protocol)
|
||||
upgrade << "Sec-WebSocket-Protocol: #{protocol}\r\n"
|
||||
end
|
||||
upgrade << "\r\n"
|
||||
upgrade << challenge_response
|
||||
|
||||
return upgrade
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def solve_challenge(first, second, third)
|
||||
# Refer to 5.2 4-9 of the draft 76
|
||||
sum = [numbers_over_spaces(first)].pack("N*") +
|
||||
[numbers_over_spaces(second)].pack("N*") +
|
||||
third
|
||||
Digest::MD5.digest(sum)
|
||||
end
|
||||
|
||||
def numbers_over_spaces(string)
|
||||
unless string
|
||||
raise HandshakeError, "WebSocket key1 or key2 is missing"
|
||||
end
|
||||
|
||||
numbers = string.scan(/[0-9]/).join.to_i
|
||||
|
||||
spaces = string.scan(/ /).size
|
||||
# As per 5.2.5, abort the connection if spaces are zero.
|
||||
raise HandshakeError, "Websocket Key1 or Key2 does not contain spaces - this is a symptom of a cross-protocol attack" if spaces == 0
|
||||
|
||||
# As per 5.2.6, abort if numbers is not an integral multiple of spaces
|
||||
if numbers % spaces != 0
|
||||
raise HandshakeError, "Invalid Key #{string.inspect}"
|
||||
end
|
||||
|
||||
quotient = numbers / spaces
|
||||
|
||||
if quotient > 2**32-1
|
||||
raise HandshakeError, "Challenge computation out of range for key #{string.inspect}"
|
||||
end
|
||||
|
||||
return quotient
|
||||
end
|
||||
|
||||
def validate_protocol!(protocol)
|
||||
raise HandshakeError, "Invalid WebSocket-Protocol: empty" if protocol.empty?
|
||||
# TODO: Validate characters
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
37
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/masking04.rb
vendored
Normal file
37
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/masking04.rb
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
class MaskedString < String
|
||||
# Read a 4 bit XOR mask - further requested bytes will be unmasked
|
||||
def read_mask
|
||||
if respond_to?(:encoding) && encoding.name != "ASCII-8BIT"
|
||||
raise "MaskedString only operates on BINARY strings"
|
||||
end
|
||||
raise "Too short" if bytesize < 4 # TODO - change
|
||||
@masking_key = String.new(self[0..3])
|
||||
end
|
||||
|
||||
# Removes the mask, behaves like a normal string again
|
||||
def unset_mask
|
||||
@masking_key = nil
|
||||
end
|
||||
|
||||
def getbyte(index)
|
||||
if defined?(@masking_key) && @masking_key
|
||||
masked_char = super
|
||||
masked_char ? masked_char ^ @masking_key.getbyte(index % 4) : nil
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def getbytes(start_index, count)
|
||||
data = ''
|
||||
data.force_encoding('ASCII-8BIT') if data.respond_to?(:force_encoding)
|
||||
count.times do |i|
|
||||
data << getbyte(start_index + i)
|
||||
end
|
||||
data
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
47
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/message_processor_03.rb
vendored
Normal file
47
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/message_processor_03.rb
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
# encoding: BINARY
|
||||
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
module MessageProcessor03
|
||||
def message(message_type, extension_data, application_data)
|
||||
case message_type
|
||||
when :close
|
||||
@close_info = {
|
||||
:code => 1005,
|
||||
:reason => "",
|
||||
:was_clean => true,
|
||||
}
|
||||
if @state == :closing
|
||||
# TODO: Check that message body matches sent data
|
||||
# We can close connection immediately since there is no more data
|
||||
# is allowed to be sent or received on this connection
|
||||
@connection.close_connection
|
||||
else
|
||||
# Acknowlege close
|
||||
# The connection is considered closed
|
||||
send_frame(:close, application_data)
|
||||
@connection.close_connection_after_writing
|
||||
end
|
||||
when :ping
|
||||
# Pong back the same data
|
||||
send_frame(:pong, application_data)
|
||||
@connection.trigger_on_ping(application_data)
|
||||
when :pong
|
||||
@connection.trigger_on_pong(application_data)
|
||||
when :text
|
||||
if application_data.respond_to?(:force_encoding)
|
||||
application_data.force_encoding("UTF-8")
|
||||
end
|
||||
@connection.trigger_on_message(application_data)
|
||||
when :binary
|
||||
@connection.trigger_on_binary(application_data)
|
||||
end
|
||||
end
|
||||
|
||||
# Ping & Pong supported
|
||||
def pingable?
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
78
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/message_processor_06.rb
vendored
Normal file
78
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/message_processor_06.rb
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
module MessageProcessor06
|
||||
def message(message_type, extension_data, application_data)
|
||||
debug [:message_received, message_type, application_data]
|
||||
|
||||
case message_type
|
||||
when :close
|
||||
status_code = case application_data.length
|
||||
when 0
|
||||
# close messages MAY contain a body
|
||||
nil
|
||||
when 1
|
||||
# Illegal close frame
|
||||
raise WSProtocolError, "Close frames with a body must contain a 2 byte status code"
|
||||
else
|
||||
application_data.slice!(0, 2).unpack('n').first
|
||||
end
|
||||
|
||||
debug [:close_frame_received, status_code, application_data]
|
||||
|
||||
@close_info = {
|
||||
:code => status_code || 1005,
|
||||
:reason => application_data,
|
||||
:was_clean => true,
|
||||
}
|
||||
|
||||
if @state == :closing
|
||||
# We can close connection immediately since no more data may be
|
||||
# sent or received on this connection
|
||||
@connection.close_connection
|
||||
elsif @state == :connected
|
||||
# Acknowlege close & echo status back to client
|
||||
# The connection is considered closed
|
||||
close_data = [status_code || 1000].pack('n')
|
||||
send_frame(:close, close_data)
|
||||
@connection.close_connection_after_writing
|
||||
end
|
||||
when :ping
|
||||
# There are a couple of protections here against malicious/broken WebSocket abusing ping frames.
|
||||
#
|
||||
# 1. Delay 200ms before replying. This reduces the number of pings from WebSocket clients behaving as
|
||||
# `for (;;) { send_ping(conn); rcv_pong(conn); }`. The spec says we "SHOULD respond with Pong frame as soon
|
||||
# as is practical".
|
||||
# 2. Reply at most every 200ms. This reduces the number of pong frames sent to WebSocket clients behaving as
|
||||
# `for (;;) { send_ping(conn); }`. The spec says "If an endpoint receives a Ping frame and has not yet sent
|
||||
# Pong frame(s) in response to previous Ping frame(s), the endpoint MAY elect to send a Pong frame for only
|
||||
# the most recently processed Ping frame."
|
||||
@most_recent_pong_application_data = application_data
|
||||
if @pong_timer == nil then
|
||||
@pong_timer = EventMachine.add_timer(0.2) do
|
||||
@pong_timer = nil
|
||||
send_frame(:pong, @most_recent_pong_application_data)
|
||||
end
|
||||
end
|
||||
@connection.trigger_on_ping(application_data)
|
||||
when :pong
|
||||
@connection.trigger_on_pong(application_data)
|
||||
when :text
|
||||
if application_data.respond_to?(:force_encoding)
|
||||
application_data.force_encoding("UTF-8")
|
||||
unless application_data.valid_encoding?
|
||||
raise InvalidDataError, "Invalid UTF8 data"
|
||||
end
|
||||
end
|
||||
@connection.trigger_on_message(application_data)
|
||||
when :binary
|
||||
@connection.trigger_on_binary(application_data)
|
||||
end
|
||||
end
|
||||
|
||||
# Ping & Pong supported
|
||||
def pingable?
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
5
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/version.rb
vendored
Normal file
5
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/version.rb
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
module EventMachine
|
||||
module Websocket
|
||||
VERSION = "0.5.2"
|
||||
end
|
||||
end
|
||||
56
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/websocket.rb
vendored
Normal file
56
vendor/bundle/ruby/2.6.0/gems/em-websocket-0.5.2/lib/em-websocket/websocket.rb
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
module EventMachine
|
||||
module WebSocket
|
||||
class << self
|
||||
attr_accessor :max_frame_size
|
||||
attr_accessor :close_timeout
|
||||
end
|
||||
@max_frame_size = 10 * 1024 * 1024 # 10MB
|
||||
# Connections are given 60s to close after being sent a close handshake
|
||||
@close_timeout = 60
|
||||
|
||||
# All errors raised by em-websocket should descend from this class
|
||||
class WebSocketError < RuntimeError; end
|
||||
|
||||
# Used for errors that occur during WebSocket handshake
|
||||
class HandshakeError < WebSocketError; end
|
||||
|
||||
# Used for errors which should cause the connection to close.
|
||||
# See RFC6455 §7.4.1 for a full description of meanings
|
||||
class WSProtocolError < WebSocketError
|
||||
def code; 1002; end
|
||||
end
|
||||
|
||||
class InvalidDataError < WSProtocolError
|
||||
def code; 1007; end
|
||||
end
|
||||
|
||||
# 1009: Message too big to process
|
||||
class WSMessageTooBigError < WSProtocolError
|
||||
def code; 1009; end
|
||||
end
|
||||
|
||||
# Start WebSocket server, including starting eventmachine run loop
|
||||
def self.start(options, &blk)
|
||||
EM.epoll
|
||||
EM.run {
|
||||
trap("TERM") { stop }
|
||||
trap("INT") { stop }
|
||||
|
||||
run(options, &blk)
|
||||
}
|
||||
end
|
||||
|
||||
# Start WebSocket server inside eventmachine run loop
|
||||
def self.run(options)
|
||||
host, port = options.values_at(:host, :port)
|
||||
EM.start_server(host, port, Connection, options) do |c|
|
||||
yield c
|
||||
end
|
||||
end
|
||||
|
||||
def self.stop
|
||||
puts "Terminating WebSocket Server"
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user