sonixlabs-em-websocket 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. data/.gitignore +4 -0
  2. data/CHANGELOG.rdoc +80 -0
  3. data/Gemfile +3 -0
  4. data/README.md +98 -0
  5. data/Rakefile +11 -0
  6. data/em-websocket.gemspec +27 -0
  7. data/examples/echo.rb +8 -0
  8. data/examples/flash_policy_file_server.rb +21 -0
  9. data/examples/js/FABridge.js +604 -0
  10. data/examples/js/WebSocketMain.swf +0 -0
  11. data/examples/js/swfobject.js +4 -0
  12. data/examples/js/web_socket.js +312 -0
  13. data/examples/multicast.rb +47 -0
  14. data/examples/test.html +30 -0
  15. data/lib/em-websocket/client_connection.rb +19 -0
  16. data/lib/em-websocket/close03.rb +11 -0
  17. data/lib/em-websocket/close05.rb +11 -0
  18. data/lib/em-websocket/close06.rb +16 -0
  19. data/lib/em-websocket/close75.rb +10 -0
  20. data/lib/em-websocket/connection.rb +184 -0
  21. data/lib/em-websocket/debugger.rb +17 -0
  22. data/lib/em-websocket/framing03.rb +167 -0
  23. data/lib/em-websocket/framing04.rb +15 -0
  24. data/lib/em-websocket/framing05.rb +168 -0
  25. data/lib/em-websocket/framing07.rb +180 -0
  26. data/lib/em-websocket/framing76.rb +114 -0
  27. data/lib/em-websocket/handler.rb +56 -0
  28. data/lib/em-websocket/handler03.rb +10 -0
  29. data/lib/em-websocket/handler05.rb +10 -0
  30. data/lib/em-websocket/handler06.rb +10 -0
  31. data/lib/em-websocket/handler07.rb +10 -0
  32. data/lib/em-websocket/handler08.rb +10 -0
  33. data/lib/em-websocket/handler13.rb +10 -0
  34. data/lib/em-websocket/handler75.rb +9 -0
  35. data/lib/em-websocket/handler76.rb +12 -0
  36. data/lib/em-websocket/handler_factory.rb +107 -0
  37. data/lib/em-websocket/handshake04.rb +75 -0
  38. data/lib/em-websocket/handshake75.rb +21 -0
  39. data/lib/em-websocket/handshake76.rb +71 -0
  40. data/lib/em-websocket/masking04.rb +63 -0
  41. data/lib/em-websocket/message_processor_03.rb +38 -0
  42. data/lib/em-websocket/message_processor_06.rb +52 -0
  43. data/lib/em-websocket/version.rb +5 -0
  44. data/lib/em-websocket/websocket.rb +45 -0
  45. data/lib/em-websocket.rb +23 -0
  46. data/lib/sonixlabs-em-websocket.rb +1 -0
  47. data/spec/helper.rb +146 -0
  48. data/spec/integration/client_examples.rb +48 -0
  49. data/spec/integration/common_spec.rb +118 -0
  50. data/spec/integration/draft03_spec.rb +270 -0
  51. data/spec/integration/draft05_spec.rb +48 -0
  52. data/spec/integration/draft06_spec.rb +88 -0
  53. data/spec/integration/draft13_spec.rb +75 -0
  54. data/spec/integration/draft75_spec.rb +117 -0
  55. data/spec/integration/draft76_spec.rb +230 -0
  56. data/spec/integration/shared_examples.rb +91 -0
  57. data/spec/unit/framing_spec.rb +325 -0
  58. data/spec/unit/handler_spec.rb +147 -0
  59. data/spec/unit/masking_spec.rb +27 -0
  60. data/spec/unit/message_processor_spec.rb +36 -0
  61. metadata +198 -0
@@ -0,0 +1,167 @@
1
+ # encoding: BINARY
2
+
3
+ module EventMachine
4
+ module WebSocket
5
+ module Framing03
6
+
7
+ # Set the max frame lenth to very high value (10MB) until there is a
8
+ # limit specified in the spec to protect against malicious attacks
9
+ MAXIMUM_FRAME_LENGTH = 10 * 1024 * 1024
10
+
11
+ def initialize_framing
12
+ @data = ''
13
+ @application_data_buffer = '' # Used for MORE frames
14
+ end
15
+
16
+ def process_data(newdata)
17
+ error = false
18
+
19
+ while !error && @data.size > 1
20
+ pointer = 0
21
+
22
+ more = ((@data.getbyte(pointer) & 0b10000000) == 0b10000000) ^ fin
23
+ # Ignoring rsv1-3 for now
24
+ opcode = @data.getbyte(0) & 0b00001111
25
+ pointer += 1
26
+
27
+ # Ignoring rsv4
28
+ length = @data.getbyte(pointer) & 0b01111111
29
+ pointer += 1
30
+
31
+ payload_length = case length
32
+ when 127 # Length defined by 8 bytes
33
+ # Check buffer size
34
+ if @data.getbyte(pointer+8-1) == nil
35
+ debug [:buffer_incomplete, @data]
36
+ error = true
37
+ next
38
+ end
39
+
40
+ # Only using the last 4 bytes for now, till I work out how to
41
+ # unpack 8 bytes. I'm sure 4GB frames will do for now :)
42
+ l = @data[(pointer+4)..(pointer+7)].unpack('N').first
43
+ pointer += 8
44
+ l
45
+ when 126 # Length defined by 2 bytes
46
+ # Check buffer size
47
+ if @data.getbyte(pointer+2-1) == nil
48
+ debug [:buffer_incomplete, @data]
49
+ error = true
50
+ next
51
+ end
52
+
53
+ l = @data[pointer..(pointer+1)].unpack('n').first
54
+ pointer += 2
55
+ l
56
+ else
57
+ length
58
+ end
59
+
60
+ # Addition to the spec to protect against malicious requests
61
+ if payload_length > MAXIMUM_FRAME_LENGTH
62
+ raise DataError, "Frame length too long (#{payload_length} bytes)"
63
+ end
64
+
65
+ # Check buffer size
66
+ if @data.getbyte(pointer+payload_length-1) == nil
67
+ debug [:buffer_incomplete, @data]
68
+ error = true
69
+ next
70
+ end
71
+
72
+ # Throw away data up to pointer
73
+ @data.slice!(0...pointer)
74
+
75
+ # Read application data
76
+ application_data = @data.slice!(0...payload_length)
77
+
78
+ frame_type = opcode_to_type(opcode)
79
+
80
+ if frame_type == :continuation && !@frame_type
81
+ raise WebSocketError, 'Continuation frame not expected'
82
+ end
83
+
84
+ if more
85
+ debug [:moreframe, frame_type, application_data]
86
+ @application_data_buffer << application_data
87
+ # The message type is passed in the first frame
88
+ @frame_type ||= frame_type
89
+ else
90
+ # Message is complete
91
+ if frame_type == :continuation
92
+ @application_data_buffer << application_data
93
+ message(@frame_type, '', @application_data_buffer)
94
+ @application_data_buffer = ''
95
+ @frame_type = nil
96
+ else
97
+ message(frame_type, '', application_data)
98
+ end
99
+ end
100
+ end # end while
101
+ end
102
+
103
+ def send_frame(frame_type, application_data)
104
+ debug [:sending_frame, frame_type, application_data]
105
+
106
+ if @state == :closing && data_frame?(frame_type)
107
+ raise WebSocketError, "Cannot send data frame since connection is closing"
108
+ end
109
+
110
+ frame = ''
111
+
112
+ opcode = type_to_opcode(frame_type)
113
+ byte1 = opcode # since more, rsv1-3 are 0
114
+ frame << byte1
115
+
116
+ length = application_data.size
117
+ if length <= 125
118
+ byte2 = length # since rsv4 is 0
119
+ frame << byte2
120
+ elsif length < 65536 # write 2 byte length
121
+ frame << 126
122
+ frame << [length].pack('n')
123
+ else # write 8 byte length
124
+ frame << 127
125
+ frame << [length >> 32, length & 0xFFFFFFFF].pack("NN")
126
+ end
127
+
128
+ frame << application_data
129
+
130
+ @connection.send_data(frame)
131
+ end
132
+
133
+ def send_text_frame(data)
134
+ send_frame(:text, data)
135
+ end
136
+
137
+ private
138
+
139
+ # This allows flipping the more bit to fin for draft 04
140
+ def fin; false; end
141
+
142
+ FRAME_TYPES = {
143
+ :continuation => 0,
144
+ :close => 1,
145
+ :ping => 2,
146
+ :pong => 3,
147
+ :text => 4,
148
+ :binary => 5
149
+ }
150
+ FRAME_TYPES_INVERSE = FRAME_TYPES.invert
151
+ # Frames are either data frames or control frames
152
+ DATA_FRAMES = [:text, :binary, :continuation]
153
+
154
+ def type_to_opcode(frame_type)
155
+ FRAME_TYPES[frame_type] || raise("Unknown frame type")
156
+ end
157
+
158
+ def opcode_to_type(opcode)
159
+ FRAME_TYPES_INVERSE[opcode] || raise(DataError, "Unknown opcode")
160
+ end
161
+
162
+ def data_frame?(type)
163
+ DATA_FRAMES.include?(type)
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: BINARY
2
+
3
+ module EventMachine
4
+ module WebSocket
5
+ # The only difference between draft 03 framing and draft 04 framing is
6
+ # that the MORE bit has been changed to a FIN bit
7
+ module Framing04
8
+ include Framing03
9
+
10
+ private
11
+
12
+ def fin; true; end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,168 @@
1
+ # encoding: BINARY
2
+
3
+ module EventMachine
4
+ module WebSocket
5
+ module Framing05
6
+
7
+ # Set the max frame lenth to very high value (10MB) until there is a
8
+ # limit specified in the spec to protect against malicious attacks
9
+ MAXIMUM_FRAME_LENGTH = 10 * 1024 * 1024
10
+
11
+ def initialize_framing
12
+ @data = MaskedString.new
13
+ @application_data_buffer = '' # Used for MORE frames
14
+ end
15
+
16
+ def process_data(newdata)
17
+ error = false
18
+
19
+ while !error && @data.size > 5 # mask plus first byte present
20
+ pointer = 0
21
+
22
+ @data.read_mask
23
+ pointer += 4
24
+
25
+ fin = (@data.getbyte(pointer) & 0b10000000) == 0b10000000
26
+ # Ignoring rsv1-3 for now
27
+ opcode = @data.getbyte(pointer) & 0b00001111
28
+ pointer += 1
29
+
30
+ # Ignoring rsv4
31
+ length = @data.getbyte(pointer) & 0b01111111
32
+ pointer += 1
33
+
34
+ payload_length = case length
35
+ when 127 # Length defined by 8 bytes
36
+ # Check buffer size
37
+ if @data.getbyte(pointer+8-1) == nil
38
+ debug [:buffer_incomplete, @data]
39
+ error = true
40
+ next
41
+ end
42
+
43
+ # Only using the last 4 bytes for now, till I work out how to
44
+ # unpack 8 bytes. I'm sure 4GB frames will do for now :)
45
+ l = @data.getbytes(pointer+4, 4).unpack('N').first
46
+ pointer += 8
47
+ l
48
+ when 126 # Length defined by 2 bytes
49
+ # Check buffer size
50
+ if @data.getbyte(pointer+2-1) == nil
51
+ debug [:buffer_incomplete, @data]
52
+ error = true
53
+ next
54
+ end
55
+
56
+ l = @data.getbytes(pointer, 2).unpack('n').first
57
+ pointer += 2
58
+ l
59
+ else
60
+ length
61
+ end
62
+
63
+ # Addition to the spec to protect against malicious requests
64
+ if payload_length > MAXIMUM_FRAME_LENGTH
65
+ raise DataError, "Frame length too long (#{payload_length} bytes)"
66
+ end
67
+
68
+ # Check buffer size
69
+ if @data.getbyte(pointer+payload_length-1) == nil
70
+ debug [:buffer_incomplete, @data]
71
+ error = true
72
+ next
73
+ end
74
+
75
+ # Read application data
76
+ application_data = @data.getbytes(pointer, payload_length)
77
+ pointer += payload_length
78
+
79
+ # Throw away data up to pointer
80
+ @data.unset_mask
81
+ @data.slice!(0...pointer)
82
+
83
+ frame_type = opcode_to_type(opcode)
84
+
85
+ if frame_type == :continuation && !@frame_type
86
+ raise WebSocketError, 'Continuation frame not expected'
87
+ end
88
+
89
+ if !fin
90
+ debug [:moreframe, frame_type, application_data]
91
+ @application_data_buffer << application_data
92
+ @frame_type = frame_type
93
+ else
94
+ # Message is complete
95
+ if frame_type == :continuation
96
+ @application_data_buffer << application_data
97
+ message(@frame_type, '', @application_data_buffer)
98
+ @application_data_buffer = ''
99
+ @frame_type = nil
100
+ else
101
+ message(frame_type, '', application_data)
102
+ end
103
+ end
104
+ end # end while
105
+ end
106
+
107
+ def send_frame(frame_type, application_data)
108
+ debug [:sending_frame, frame_type, application_data]
109
+
110
+ if @state == :closing && data_frame?(frame_type)
111
+ raise WebSocketError, "Cannot send data frame since connection is closing"
112
+ end
113
+
114
+ frame = ''
115
+
116
+ opcode = type_to_opcode(frame_type)
117
+ byte1 = opcode | 0b10000000 # fin bit set, rsv1-3 are 0
118
+ frame << byte1
119
+
120
+ length = application_data.size
121
+ if length <= 125
122
+ byte2 = length # since rsv4 is 0
123
+ frame << byte2
124
+ elsif length < 65536 # write 2 byte length
125
+ frame << 126
126
+ frame << [length].pack('n')
127
+ else # write 8 byte length
128
+ frame << 127
129
+ frame << [length >> 32, length & 0xFFFFFFFF].pack("NN")
130
+ end
131
+
132
+ frame << application_data
133
+
134
+ @connection.send_data(frame)
135
+ end
136
+
137
+ def send_text_frame(data)
138
+ send_frame(:text, data)
139
+ end
140
+
141
+ private
142
+
143
+ FRAME_TYPES = {
144
+ :continuation => 0,
145
+ :close => 1,
146
+ :ping => 2,
147
+ :pong => 3,
148
+ :text => 4,
149
+ :binary => 5
150
+ }
151
+ FRAME_TYPES_INVERSE = FRAME_TYPES.invert
152
+ # Frames are either data frames or control frames
153
+ DATA_FRAMES = [:text, :binary, :continuation]
154
+
155
+ def type_to_opcode(frame_type)
156
+ FRAME_TYPES[frame_type] || raise("Unknown frame type")
157
+ end
158
+
159
+ def opcode_to_type(opcode)
160
+ FRAME_TYPES_INVERSE[opcode] || raise(DataError, "Unknown opcode")
161
+ end
162
+
163
+ def data_frame?(type)
164
+ DATA_FRAMES.include?(type)
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,180 @@
1
+ # encoding: BINARY
2
+
3
+ module EventMachine
4
+ module WebSocket
5
+ module Framing07
6
+
7
+ attr_accessor :mask_outbound_messages, :require_masked_inbound_messages
8
+
9
+ def initialize_framing
10
+ @data = MaskedString.new
11
+ @application_data_buffer = '' # Used for MORE frames
12
+ @mask_outbound_messages = false
13
+ @require_masked_inbound_messages = true
14
+ end
15
+
16
+ def process_data(newdata)
17
+ error = false
18
+
19
+ while !error && @data.size >= 2
20
+ pointer = 0
21
+
22
+ fin = (@data.getbyte(pointer) & 0b10000000) == 0b10000000
23
+ # Ignoring rsv1-3 for now
24
+ opcode = @data.getbyte(pointer) & 0b00001111
25
+ pointer += 1
26
+
27
+ mask = (@data.getbyte(pointer) & 0b10000000) == 0b10000000
28
+ length = @data.getbyte(pointer) & 0b01111111
29
+ pointer += 1
30
+
31
+ if require_masked_inbound_messages
32
+ raise WebSocketError, 'Data from client must be masked' unless mask
33
+ end
34
+
35
+ payload_length = case length
36
+ when 127 # Length defined by 8 bytes
37
+ # Check buffer size
38
+ if @data.getbyte(pointer+8-1) == nil
39
+ debug [:buffer_incomplete, @data]
40
+ error = true
41
+ next
42
+ end
43
+
44
+ # Only using the last 4 bytes for now, till I work out how to
45
+ # unpack 8 bytes. I'm sure 4GB frames will do for now :)
46
+ l = @data.getbytes(pointer+4, 4).unpack('N').first
47
+ pointer += 8
48
+ l
49
+ when 126 # Length defined by 2 bytes
50
+ # Check buffer size
51
+ if @data.getbyte(pointer+2-1) == nil
52
+ debug [:buffer_incomplete, @data]
53
+ error = true
54
+ next
55
+ end
56
+
57
+ l = @data.getbytes(pointer, 2).unpack('n').first
58
+ pointer += 2
59
+ l
60
+ else
61
+ length
62
+ end
63
+
64
+ # Compute the expected frame length
65
+ frame_length = pointer + payload_length
66
+ frame_length += 4 if mask
67
+
68
+ # Check buffer size
69
+ if @data.getbyte(frame_length - 1) == nil
70
+ debug [:buffer_incomplete, @data]
71
+ error = true
72
+ next
73
+ end
74
+
75
+ # Remove frame header
76
+ @data.slice!(0...pointer)
77
+ pointer = 0
78
+
79
+ # Read application data (unmasked if required)
80
+ @data.read_mask if mask
81
+ pointer += 4 if mask
82
+ application_data = @data.getbytes(pointer, payload_length)
83
+ pointer += payload_length
84
+ @data.unset_mask if mask
85
+
86
+ # Throw away data up to pointer
87
+ @data.slice!(0...pointer)
88
+
89
+ frame_type = opcode_to_type(opcode)
90
+
91
+ if frame_type == :continuation && !@frame_type
92
+ raise WebSocketError, 'Continuation frame not expected'
93
+ end
94
+
95
+ if !fin
96
+ debug [:moreframe, frame_type, application_data]
97
+ @application_data_buffer << application_data
98
+ # The message type is passed in the first frame
99
+ @frame_type ||= frame_type
100
+ else
101
+ # Message is complete
102
+ if frame_type == :continuation
103
+ @application_data_buffer << application_data
104
+ message(@frame_type, '', @application_data_buffer)
105
+ @application_data_buffer = ''
106
+ @frame_type = nil
107
+ else
108
+ message(frame_type, '', application_data)
109
+ end
110
+ end
111
+ end # end while
112
+ end
113
+
114
+ def send_frame(frame_type, application_data)
115
+ debug [:sending_frame, frame_type, application_data]
116
+
117
+ if @state == :closing && data_frame?(frame_type)
118
+ raise WebSocketError, "Cannot send data frame since connection is closing"
119
+ end
120
+
121
+ frame = ''
122
+
123
+ opcode = type_to_opcode(frame_type)
124
+ byte1 = opcode | 0b10000000 # fin bit set, rsv1-3 are 0
125
+ frame << byte1
126
+
127
+ mask = mask_outbound_messages ? 0b10000000 : 0b00000000 # must be masked if from client
128
+ length = application_data.size
129
+ if length <= 125
130
+ byte2 = length # since rsv4 is 0
131
+ frame << (mask | byte2)
132
+ elsif length < 65536 # write 2 byte length
133
+ frame << (mask | 126)
134
+ frame << [length].pack('n')
135
+ else # write 8 byte length
136
+ frame << (mask | 127)
137
+ frame << [length >> 32, length & 0xFFFFFFFF].pack("NN")
138
+ end
139
+
140
+ if mask_outbound_messages
141
+ frame << MaskedString.create_masked_string(application_data)
142
+ else
143
+ frame << application_data
144
+ end
145
+
146
+ @connection.send_data(frame)
147
+ end
148
+
149
+ def send_text_frame(data)
150
+ send_frame(:text, data)
151
+ end
152
+
153
+ private
154
+
155
+ FRAME_TYPES = {
156
+ :continuation => 0,
157
+ :text => 1,
158
+ :binary => 2,
159
+ :close => 8,
160
+ :ping => 9,
161
+ :pong => 10,
162
+ }
163
+ FRAME_TYPES_INVERSE = FRAME_TYPES.invert
164
+ # Frames are either data frames or control frames
165
+ DATA_FRAMES = [:text, :binary, :continuation]
166
+
167
+ def type_to_opcode(frame_type)
168
+ FRAME_TYPES[frame_type] || raise("Unknown frame type")
169
+ end
170
+
171
+ def opcode_to_type(opcode)
172
+ FRAME_TYPES_INVERSE[opcode] || raise(DataError, "Unknown opcode")
173
+ end
174
+
175
+ def data_frame?(type)
176
+ DATA_FRAMES.include?(type)
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,114 @@
1
+ # encoding: BINARY
2
+
3
+ module EventMachine
4
+ module WebSocket
5
+ module Framing76
6
+
7
+ # Set the max frame lenth to very high value (10MB) until there is a
8
+ # limit specified in the spec to protect against malicious attacks
9
+ MAXIMUM_FRAME_LENGTH = 10 * 1024 * 1024
10
+
11
+ def initialize_framing
12
+ @data = ''
13
+ end
14
+
15
+ def process_data(newdata)
16
+ debug [:message, @data]
17
+
18
+ # This algorithm comes straight from the spec
19
+ # http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#section-5.3
20
+
21
+ error = false
22
+
23
+ while !error
24
+ return if @data.size == 0
25
+
26
+ pointer = 0
27
+ frame_type = @data.getbyte(pointer)
28
+ pointer += 1
29
+
30
+ if (frame_type & 0x80) == 0x80
31
+ # If the high-order bit of the /frame type/ byte is set
32
+ length = 0
33
+
34
+ loop do
35
+ return false if !@data.getbyte(pointer)
36
+ b = @data.getbyte(pointer)
37
+ pointer += 1
38
+ b_v = b & 0x7F
39
+ length = length * 128 + b_v
40
+ break unless (b & 0x80) == 0x80
41
+ end
42
+
43
+ # Addition to the spec to protect against malicious requests
44
+ if length > MAXIMUM_FRAME_LENGTH
45
+ raise DataError, "Frame length too long (#{length} bytes)"
46
+ end
47
+
48
+ if @data.getbyte(pointer+length-1) == nil
49
+ debug [:buffer_incomplete, @data]
50
+ # Incomplete data - leave @data to accumulate
51
+ error = true
52
+ else
53
+ # Straight from spec - I'm sure this isn't crazy...
54
+ # 6. Read /length/ bytes.
55
+ # 7. Discard the read bytes.
56
+ @data = @data[(pointer+length)..-1]
57
+
58
+ # If the /frame type/ is 0xFF and the /length/ was 0, then close
59
+ if length == 0
60
+ @connection.send_data("\xff\x00")
61
+ @state = :closing
62
+ @connection.close_connection_after_writing
63
+ else
64
+ error = true
65
+ end
66
+ end
67
+ else
68
+ # If the high-order bit of the /frame type/ byte is _not_ set
69
+
70
+ if @data.getbyte(0) != 0x00
71
+ # Close the connection since this buffer can never match
72
+ raise DataError, "Invalid frame received"
73
+ end
74
+
75
+ # Addition to the spec to protect against malicious requests
76
+ if @data.size > MAXIMUM_FRAME_LENGTH
77
+ raise DataError, "Frame length too long (#{@data.size} bytes)"
78
+ end
79
+
80
+ # Optimization to avoid calling slice! unnecessarily
81
+ error = true and next unless newdata =~ /\xff/
82
+
83
+ msg = @data.slice!(/\A\x00[^\xff]*\xff/)
84
+ if msg
85
+ msg.gsub!(/\A\x00|\xff\z/, '')
86
+ if @state == :closing
87
+ debug [:ignored_message, msg]
88
+ else
89
+ msg.force_encoding('UTF-8') if msg.respond_to?(:force_encoding)
90
+ @connection.trigger_on_message(msg)
91
+ end
92
+ else
93
+ error = true
94
+ end
95
+ end
96
+ end
97
+
98
+ false
99
+ end
100
+
101
+ # frames need to start with 0x00-0x7f byte and end with
102
+ # an 0xFF byte. Per spec, we can also set the first
103
+ # byte to a value betweent 0x80 and 0xFF, followed by
104
+ # a leading length indicator
105
+ def send_text_frame(data)
106
+ debug [:sending_text_frame, data]
107
+ ary = ["\x00", data, "\xff"]
108
+ ary.collect{ |s| s.force_encoding('UTF-8') if s.respond_to?(:force_encoding) }
109
+ @connection.send_data(ary.join)
110
+ end
111
+
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,56 @@
1
+ module EventMachine
2
+ module WebSocket
3
+ class Handler
4
+ include Debugger
5
+
6
+ attr_reader :request, :state
7
+
8
+ def initialize(connection, request, debug = false)
9
+ @connection, @request = connection, request
10
+ @debug = debug
11
+ @state = :handshake
12
+ initialize_framing
13
+ end
14
+
15
+ def run_server
16
+ @connection.send_data handshake_server
17
+ @state = :connected
18
+ @connection.trigger_on_open
19
+ end
20
+
21
+ def run_client
22
+ self.mask_outbound_messages = true
23
+ self.require_masked_inbound_messages = false
24
+ @connection.send_data handshake_client
25
+ end
26
+
27
+ # Handshake response
28
+ def handshake
29
+ # Implemented in subclass
30
+ end
31
+
32
+ def handshake_server
33
+ handshake #backwards compatibility
34
+ end
35
+
36
+ # Handshake initiation
37
+ def handshake_client
38
+ # Implemented in subclass
39
+ end
40
+
41
+ def receive_data(data)
42
+ @data << data
43
+ process_data(data)
44
+ end
45
+
46
+ def close_websocket(code, body)
47
+ # Implemented in subclass
48
+ end
49
+
50
+ def unbind
51
+ @state = :closed
52
+ @connection.trigger_on_close
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,10 @@
1
+ module EventMachine
2
+ module WebSocket
3
+ class Handler03 < Handler
4
+ include Handshake76
5
+ include Framing03
6
+ include MessageProcessor03
7
+ include Close03
8
+ end
9
+ end
10
+ end