Class: Protocol::WebSocket::Connection

Inherits:
Object
  • Object
show all
Defined in:
lib/protocol/websocket/connection.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(framer, mask: nil) ⇒ Connection

Returns a new instance of Connection.

Parameters:

  • mask (Hash) (defaults to: nil)

    a customizable set of options

Options Hash (mask:):

  • 4-byte (String)

    mask to be used for frames generated by this connection.



28
29
30
31
32
33
34
35
36
37
38
# File 'lib/protocol/websocket/connection.rb', line 28

def initialize(framer, mask: nil)
	if mask == true
		mask = SecureRandom.bytes(4)
	end
	
	@framer = framer
	@mask = mask
	
	@state = :open
	@frames = []
end

Instance Attribute Details

#framerObject (readonly)

The framer which is used for reading and writing frames.



41
42
43
# File 'lib/protocol/websocket/connection.rb', line 41

def framer
  @framer
end

#framesObject

Buffered frames which form part of a complete message.



47
48
49
# File 'lib/protocol/websocket/connection.rb', line 47

def frames
  @frames
end

#maskObject (readonly)

The (optional) mask which is used when generating frames.



44
45
46
# File 'lib/protocol/websocket/connection.rb', line 44

def mask
  @mask
end

Instance Method Details

#closeObject



57
58
59
60
61
# File 'lib/protocol/websocket/connection.rb', line 57

def close
	send_close unless closed?
	
	@framer.close
end

#closed?Boolean

Returns:

  • (Boolean)


53
54
55
# File 'lib/protocol/websocket/connection.rb', line 53

def closed?
	@state == :closed
end

#flushObject



49
50
51
# File 'lib/protocol/websocket/connection.rb', line 49

def flush
	@framer.flush
end

#next_messageObject

Deprecated.



198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/protocol/websocket/connection.rb', line 198

def next_message
	@framer.flush
	
	while read_frame
		if @frames.last&.finished?
			frames = @frames
			@frames = []
			
			return frames
		end
	end
end

#open!Object



156
157
158
159
160
# File 'lib/protocol/websocket/connection.rb', line 156

def open!
	@state = :open
	
	return self
end

#readString

Returns a unicode or binary string.

Returns:

  • (String)

    a unicode or binary string.



184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/protocol/websocket/connection.rb', line 184

def read
	@framer.flush
	
	while read_frame
		if @frames.last&.finished?
			buffer = @frames.map(&:unpack).join
			@frames = []
			
			return buffer
		end
	end
end

#read_frameObject



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/protocol/websocket/connection.rb', line 63

def read_frame
	return nil if closed?
	
	frame = @framer.read_frame
	
	yield frame if block_given?
	
	frame.apply(self)
	
	return frame
rescue ProtocolError => error
	send_close(error.code, error.message)
	
	raise
rescue
	send_close(Error::PROTOCOL_ERROR, $!.message)
	
	raise
end

#receive_binary(frame) ⇒ Object



95
96
97
98
99
100
101
# File 'lib/protocol/websocket/connection.rb', line 95

def receive_binary(frame)
	if @frames.empty?
		@frames << frame
	else
		raise ProtocolError, "Received binary, but expecting continuation!"
	end
end

#receive_close(frame) ⇒ Object



135
136
137
138
139
140
141
142
143
# File 'lib/protocol/websocket/connection.rb', line 135

def receive_close(frame)
	@state = :closed
	
	code, message = frame.unpack
	
	if code and code != Error::NO_ERROR
		raise ClosedError.new message, code
	end
end

#receive_continuation(frame) ⇒ Object



103
104
105
106
107
108
109
# File 'lib/protocol/websocket/connection.rb', line 103

def receive_continuation(frame)
	if @frames.any?
		@frames << frame
	else
		raise ProtocolError, "Received unexpected continuation!"
	end
end

#receive_frame(frame) ⇒ Object



170
171
172
# File 'lib/protocol/websocket/connection.rb', line 170

def receive_frame(frame)
	warn "Unhandled frame #{frame.inspect}"
end

#receive_ping(frame) ⇒ Object



162
163
164
165
166
167
168
# File 'lib/protocol/websocket/connection.rb', line 162

def receive_ping(frame)
	if @state != :closed
		write_frame(frame.reply)
	else
		raise ProtocolError, "Cannot receive ping in state #{@state}"
	end
end

#receive_text(frame) ⇒ Object



87
88
89
90
91
92
93
# File 'lib/protocol/websocket/connection.rb', line 87

def receive_text(frame)
	if @frames.empty?
		@frames << frame
	else
		raise ProtocolError, "Received text, but expecting continuation!"
	end
end

#send_binary(buffer) ⇒ Object



118
119
120
121
122
123
# File 'lib/protocol/websocket/connection.rb', line 118

def send_binary(buffer)
	frame = BinaryFrame.new(mask: @mask)
	frame.pack buffer
	
	write_frame(frame)
end

#send_close(code = Error::NO_ERROR, message = nil) ⇒ Object



125
126
127
128
129
130
131
132
133
# File 'lib/protocol/websocket/connection.rb', line 125

def send_close(code = Error::NO_ERROR, message = nil)
	frame = CloseFrame.new(mask: @mask)
	frame.pack(code, message)
	
	self.write_frame(frame)
	self.flush
	
	@state = :closed
end

#send_ping(data = "") ⇒ Object



145
146
147
148
149
150
151
152
153
154
# File 'lib/protocol/websocket/connection.rb', line 145

def send_ping(data = "")
	if @state != :closed
		frame = PingFrame.new(mask: @mask)
		frame.pack(data)
		
		write_frame(frame)
	else
		raise ProtocolError, "Cannot send ping in state #{@state}"
	end
end

#send_text(buffer) ⇒ Object



111
112
113
114
115
116
# File 'lib/protocol/websocket/connection.rb', line 111

def send_text(buffer)
	frame = TextFrame.new(mask: @mask)
	frame.pack buffer
	
	write_frame(frame)
end

#write(buffer) ⇒ Object

Parameters:

  • buffer (String)

    a unicode or binary string.



175
176
177
178
179
180
181
# File 'lib/protocol/websocket/connection.rb', line 175

def write(buffer)
	if buffer.encoding == Encoding::UTF_8
		send_text(buffer)
	else
		send_data(buffer)
	end
end

#write_frame(frame) ⇒ Object



83
84
85
# File 'lib/protocol/websocket/connection.rb', line 83

def write_frame(frame)
	@framer.write_frame(frame)
end