Class: WebSocket::Driver

Inherits:
Object
  • Object
show all
Includes:
EventEmitter
Defined in:
lib/websocket/driver.rb,
lib/websocket/driver/hybi.rb,
lib/websocket/driver/proxy.rb,
lib/websocket/driver/client.rb,
lib/websocket/driver/server.rb,
lib/websocket/driver/draft75.rb,
lib/websocket/driver/draft76.rb,
lib/websocket/driver/headers.rb,
lib/websocket/driver/hybi/frame.rb,
lib/websocket/driver/hybi/message.rb,
lib/websocket/driver/event_emitter.rb,
lib/websocket/driver/stream_reader.rb

Direct Known Subclasses

Draft75, Hybi, Server

Defined Under Namespace

Modules: EventEmitter Classes: Client, CloseEvent, ConnectEvent, Draft75, Draft76, Headers, Hybi, MessageEvent, OpenEvent, PingEvent, PongEvent, Proxy, Server, StreamReader

Constant Summary collapse

MAX_LENGTH =
0x3ffffff
PORTS =
{ 'ws' => 80, 'wss' => 443 }
STATES =
[:connecting, :open, :closing, :closed]
ProtocolError =
Class.new(StandardError)
URIError =
Class.new(ArgumentError)
ConfigurationError =
Class.new(ArgumentError)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from EventEmitter

#add_listener, #emit, #listener_count, #listeners, #on, #remove_all_listeners, #remove_listener

Constructor Details

#initialize(socket, options = {}) ⇒ Driver

Returns a new instance of Driver.



72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/websocket/driver.rb', line 72

def initialize(socket, options = {})
  super()
  Driver.validate_options(options, [:max_length, :masking, :require_masking, :protocols, :binary_data_format])

  @socket      = socket
  @reader      = StreamReader.new
  @options     = options
  @max_length  = options[:max_length] || MAX_LENGTH
  @headers     = Headers.new
  @queue       = []
  @ready_state = 0

  @binary_data_format = options[:binary_data_format] || :string
end

Instance Attribute Details

#protocolObject (readonly)

Returns the value of attribute protocol.



70
71
72
# File 'lib/websocket/driver.rb', line 70

def protocol
  @protocol
end

#ready_stateObject (readonly)

Returns the value of attribute ready_state.



70
71
72
# File 'lib/websocket/driver.rb', line 70

def ready_state
  @ready_state
end

Class Method Details

.client(socket, options = {}) ⇒ Object



176
177
178
# File 'lib/websocket/driver.rb', line 176

def self.client(socket, options = {})
  Client.new(socket, options.merge(:masking => true))
end

.encode(data, encoding = nil) ⇒ Object



200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/websocket/driver.rb', line 200

def self.encode(data, encoding = nil)
  if Array === data
    data = data.pack('C*')
    encoding ||= Encoding::BINARY
  end

  return data if encoding.nil? or data.encoding == encoding

  if data.encoding == Encoding::BINARY
    data = data.dup if data.frozen?
    data.force_encoding(encoding)
  else
    data.encode(encoding)
  end
end

.host_header(uri) ⇒ Object



216
217
218
219
220
221
222
# File 'lib/websocket/driver.rb', line 216

def self.host_header(uri)
  host = uri.host
  if uri.port and uri.port != PORTS[uri.scheme]
    host += ":#{uri.port}"
  end
  host
end

.rack(socket, options = {}) ⇒ Object



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/websocket/driver.rb', line 184

def self.rack(socket, options = {})
  env     = socket.env
  version = env['HTTP_SEC_WEBSOCKET_VERSION']
  key     = env['HTTP_SEC_WEBSOCKET_KEY']
  key1    = env['HTTP_SEC_WEBSOCKET_KEY1']
  key2    = env['HTTP_SEC_WEBSOCKET_KEY2']

  if version or key
    Hybi.new(socket, options.merge(:require_masking => true))
  elsif key1 or key2
    Draft76.new(socket, options)
  else
    Draft75.new(socket, options)
  end
end

.server(socket, options = {}) ⇒ Object



180
181
182
# File 'lib/websocket/driver.rb', line 180

def self.server(socket, options = {})
  Server.new(socket, options.merge(:require_masking => true))
end

.validate_options(options, valid_keys) ⇒ Object



224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/websocket/driver.rb', line 224

def self.validate_options(options, valid_keys)
  options.keys.each do |key|
    unless valid_keys.include?(key)
      raise ConfigurationError, "Unrecognized option: #{ key.inspect }"
    end
  end

  if options[:binary_data_format]
    unless [:array, :string].include?(options[:binary_data_format])
      raise ConfigurationError, "Invalid :binary_data_format: #{options[:binary_data_format].inspect}"
    end
  end
end

.websocket?(env) ⇒ Boolean

Returns:

  • (Boolean)


238
239
240
241
242
243
244
245
# File 'lib/websocket/driver.rb', line 238

def self.websocket?(env)
  connection = env['HTTP_CONNECTION'] || ''
  upgrade    = env['HTTP_UPGRADE']    || ''

  env['REQUEST_METHOD'] == 'GET' and
  connection.downcase.split(/ *, */).include?('upgrade') and
  upgrade.downcase == 'websocket'
end

Instance Method Details

#add_extension(extension) ⇒ Object



92
93
94
# File 'lib/websocket/driver.rb', line 92

def add_extension(extension)
  false
end

#binary(message) ⇒ Object



125
126
127
# File 'lib/websocket/driver.rb', line 125

def binary(message)
  false
end

#close(reason = nil, code = nil) ⇒ Object



137
138
139
140
141
142
# File 'lib/websocket/driver.rb', line 137

def close(reason = nil, code = nil)
  return false unless @ready_state == 1
  @ready_state = 3
  emit(:close, CloseEvent.new(nil, nil))
  true
end

#ping(*args) ⇒ Object



129
130
131
# File 'lib/websocket/driver.rb', line 129

def ping(*args)
  false
end

#pong(*args) ⇒ Object



133
134
135
# File 'lib/websocket/driver.rb', line 133

def pong(*args)
  false
end

#set_header(name, value) ⇒ Object



96
97
98
99
100
# File 'lib/websocket/driver.rb', line 96

def set_header(name, value)
  return false unless @ready_state <= 0
  @headers[name] = value
  true
end

#startObject



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/websocket/driver.rb', line 102

def start
  return false unless @ready_state == 0

  unless Driver.websocket?(@socket.env)
    return fail_handshake(ProtocolError.new('Not a WebSocket request'))
  end

  begin
    response = handshake_response
  rescue => error
    return fail_handshake(error)
  end

  @socket.write(response)
  open unless @stage == -1
  true
end

#stateObject



87
88
89
90
# File 'lib/websocket/driver.rb', line 87

def state
  return nil unless @ready_state >= 0
  STATES[@ready_state]
end

#text(message) ⇒ Object



120
121
122
123
# File 'lib/websocket/driver.rb', line 120

def text(message)
  message = Driver.encode(message, Encoding::UTF_8)
  frame(message, :text)
end