websocket_parser 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in websocket_parser.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Alberto Fernandez-Capel
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # WebSocketParser
2
+
3
+ WebsocketParser is a RFC6455 compliant parser for websocket messages written in Ruby. It
4
+ is intended to write websockets servers in Ruby, but it only handles the parsing of the
5
+ WebSocket protocol, leaving I/O to the server.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'websocket_parser'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install websocket_parser
20
+
21
+ ## Usage
22
+
23
+ ```
24
+ require 'websocket_parser'
25
+
26
+ socket = # Handle I/O with your server/event loop.
27
+
28
+ parser = WebSocket::Parser.new
29
+
30
+ parser.on_message do |m|
31
+ puts "Received message #{m}"
32
+ end
33
+
34
+ parser.on_error do |m|
35
+ puts "Received error #{m}"
36
+ socket.close!
37
+ end
38
+
39
+ parser.on_close do |m|
40
+ puts "Client closed connection. Reason: #{m}"
41
+ socket.close!
42
+ end
43
+
44
+ parser.on_ping do |m|
45
+ socket << WebSocket::Message.pong.to_data
46
+ end
47
+
48
+ parser << socket.read(4096)
49
+
50
+ # To send a message:
51
+
52
+ socket << WebSocket::Message.new('Hi there!').to_data
53
+
54
+ ```
55
+
56
+ ## Contributing
57
+
58
+ 1. Fork it
59
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
60
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
61
+ 4. Push to the branch (`git push origin my-new-feature`)
62
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,36 @@
1
+ require 'digest/sha1'
2
+
3
+ module WebSocket
4
+ class ClientHandshake < Http::Request
5
+
6
+ def initialize(method, uri, headers = {}, proxy = {}, body = nil, version = "1.1")
7
+ @method = method.to_s.downcase.to_sym
8
+ @uri = uri.is_a?(URI) ? uri : URI(uri.to_s)
9
+
10
+ @headers = headers
11
+ @proxy, @body, @version = proxy, body, version
12
+ end
13
+
14
+ def valid?
15
+ headers['Connection'] == 'Upgrade' &&
16
+ headers['Upgrade'] == 'websocket' &&
17
+ headers['Sec-WebSocket-Version'].to_i == PROTOCOL_VERSION
18
+ end
19
+
20
+ def accept_response
21
+ response_headers = {
22
+ 'Upgrade' => 'websocket',
23
+ 'Connection' => 'Upgrade',
24
+ 'Sec-WebSocket-Accept' => accept_token
25
+ }
26
+
27
+ ServerHandshake.new(101, '1.1', response_headers)
28
+ end
29
+
30
+ private
31
+
32
+ def accept_token
33
+ Digest::SHA1.base64digest(headers['Sec-WebSocket-Key'].strip + GUID)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,74 @@
1
+ module WebSocket
2
+ class Message
3
+ attr_reader :type, :payload
4
+
5
+ # Return a new ping message
6
+ def self.ping(message = '')
7
+ new(message, :ping)
8
+ end
9
+
10
+ # Return a new pong message
11
+ def self.pong(message = '')
12
+ new(message, :pong)
13
+ end
14
+
15
+ # Return a new close message
16
+ def self.close(reason = '')
17
+ new(reason, :close)
18
+ end
19
+
20
+ def initialize(message, type = :text)
21
+ @type, @payload = type, message.force_encoding("ASCII-8BIT")
22
+ end
23
+
24
+ def first_byte
25
+ @first_byte ||= if type == :continuation
26
+ OPCODE_VALUES[type]
27
+ else
28
+ 0b10000000 | OPCODE_VALUES[type] # set FIN bit to true
29
+ end
30
+ end
31
+
32
+ def message_size
33
+ if payload_length < 126
34
+ :small
35
+ elsif payload_length < 65_536 # fits in 2 bytes
36
+ :medium
37
+ else
38
+ :large
39
+ end
40
+ end
41
+
42
+ def second_byte
43
+ case message_size
44
+ when :small then payload_length
45
+ when :medium then 126
46
+ when :large then 127
47
+ end
48
+ end
49
+
50
+ def payload_length
51
+ @payload.length
52
+ end
53
+
54
+ def extended_payload_length
55
+ message_size == :small ? nil : payload_length
56
+ end
57
+
58
+ def to_a
59
+ [first_byte, second_byte, extended_payload_length, payload].compact
60
+ end
61
+
62
+ def pack_format
63
+ "#{FRAME_FORMAT[message_size]}#{payload_length}"
64
+ end
65
+
66
+ def to_data
67
+ to_a.pack(pack_format)
68
+ end
69
+
70
+ def write(io)
71
+ io << to_data
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,239 @@
1
+ module WebSocket
2
+ #
3
+ # This class parses WebSocket messages and frames.
4
+ #
5
+ # Each message is divied in frames as described in RFC 6455.
6
+ #
7
+ # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
8
+ # +-+-+-+-+-------+-+-------------+-------------------------------+
9
+ # |F|R|R|R| opcode|M| Payload len | Extended payload length |
10
+ # |I|S|S|S| (4) |A| (7) | (16/64) |
11
+ # |N|V|V|V| |S| | (if payload len==126/127) |
12
+ # | |1|2|3| |K| | |
13
+ # +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
14
+ # | Extended payload length continued, if payload len == 127 |
15
+ # + - - - - - - - - - - - - - - - +-------------------------------+
16
+ # | |Masking-key, if MASK set to 1 |
17
+ # +-------------------------------+-------------------------------+
18
+ # | Masking-key (continued) | Payload Data |
19
+ # +-------------------------------- - - - - - - - - - - - - - - - +
20
+ # : Payload Data continued ... :
21
+ # + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
22
+ # | Payload Data continued ... |
23
+ # +---------------------------------------------------------------+
24
+ #
25
+ # for more info on the frame format see: http://tools.ietf.org/html/rfc6455#section-5
26
+ #
27
+ class Parser
28
+ def initialize
29
+ @data = ''.force_encoding("ASCII-8BIT")
30
+
31
+ @on_message = Proc.new do |msg|
32
+ puts "Received message: #{msg}"
33
+ end
34
+
35
+ @on_error = Proc.new do |error|
36
+ puts "WebSocket error: #{error}"
37
+ end
38
+
39
+ @on_close = Proc.new do |reason|
40
+ puts "Should close connection. Reason: #{reason}"
41
+ end
42
+
43
+ @on_ping = Proc.new do |ping|
44
+ puts "Ping received"
45
+ end
46
+
47
+ @on_pong = Proc.new do |pong|
48
+ puts "Pong received"
49
+ end
50
+
51
+ @state = :header
52
+ end
53
+
54
+ def on_message(&callback)
55
+ @on_message = callback
56
+ end
57
+
58
+ def on_error(&callback)
59
+ @on_error = callback
60
+ end
61
+
62
+ def on_close(&callback)
63
+ @on_close = callback
64
+ end
65
+
66
+ def on_ping(&callback)
67
+ @on_ping = callback
68
+ end
69
+
70
+ def on_pong(&callback)
71
+ @on_pong = callback
72
+ end
73
+
74
+ def receive(data)
75
+ @data << data
76
+
77
+ read_header if @state == :header
78
+ read_payload_length if @state == :payload_length
79
+ read_payload if @state == :payload
80
+
81
+ process_frame if @state == :complete
82
+ end
83
+
84
+ alias_method :<<, :receive
85
+
86
+ def read_header
87
+ return unless @data.length >= 2 # Not enough data
88
+
89
+ @first_byte, @second_byte = @data.slice!(0,2).unpack('C2')
90
+ @state = :payload_length
91
+ end
92
+
93
+ def read_payload_length
94
+ @payload_length = if message_size == :small
95
+ payload_length_field
96
+ else
97
+ read_extended_payload_length
98
+ end
99
+
100
+ @state = :payload if @payload_length
101
+ end
102
+
103
+ def read_extended_payload_length
104
+ if message_size == :medium && @data.size >= 2
105
+ unpack_bytes(2,'S<')
106
+ elsif message_size == :large && @data.size >= 4
107
+ unpack_bytes(8,'Q<')
108
+ end
109
+ end
110
+
111
+ def read_payload
112
+ return unless @data.length >= @payload_length # Not enough data
113
+
114
+ payload_data = unpack_bytes(@payload_length, "a#{@payload_length}")
115
+ @payload = masked? ? unmask(payload_data) : payload_data
116
+
117
+ @state = :complete if @payload
118
+ end
119
+
120
+ def unpack_bytes(num, format)
121
+ @data.slice!(0,num).unpack(format).first
122
+ end
123
+
124
+ def completed_message?
125
+ fin? || payload
126
+ end
127
+
128
+ def control_frame?
129
+ [:close, :ping, :pong].include?(opcode)
130
+ end
131
+
132
+ def process_frame
133
+ if @current_message
134
+ @current_message << @payload
135
+ else
136
+ @current_message = @payload
137
+ end
138
+
139
+ if fin?
140
+ process_message
141
+ end
142
+
143
+ reset_frame!
144
+ end
145
+
146
+ def process_message
147
+ case opcode
148
+ when :text
149
+ @on_message.call(@current_message.force_encoding("UTF-8"))
150
+ when :binary
151
+ @on_message.call(@current_message)
152
+ when :close
153
+ @on_close.call(@current_message.encode("UTF-8"))
154
+ when :ping
155
+ @on_ping.call(@current_message.encode("UTF-8"))
156
+ when :pong
157
+ @on_pong.call(@current_message.encode("UTF-8"))
158
+ end
159
+
160
+ @current_message = nil
161
+ end
162
+
163
+ # Whether the FIN bit is set. The FIN bit indicates that
164
+ # this is the final fragment in a message
165
+ def fin?
166
+ @first_byte & 0b10000000 != 0
167
+ end
168
+
169
+ def svr1?
170
+ @first_byte & 0b01000000 != 0
171
+ end
172
+
173
+ def svr2?
174
+ @first_byte & 0b00100000 != 0
175
+ end
176
+
177
+ def svr3?
178
+ @first_byte & 0b00010000 != 0
179
+ end
180
+
181
+ def opcode
182
+ @opcode ||= OPCODES[@first_byte & 0b00001111]
183
+ end
184
+
185
+ def masked?
186
+ @second_byte & 0b10000000 != 0
187
+ end
188
+
189
+ def payload_length_field
190
+ @second_byte & 0b01111111
191
+ end
192
+
193
+ def mask_key
194
+ @mask_key ||= if masked?
195
+ @second_byte && read_uint32!
196
+ else
197
+ nil
198
+ end
199
+ end
200
+
201
+ def message_size
202
+ if payload_length_field < 126
203
+ :small
204
+ elsif payload_length_field == 126
205
+ :medium
206
+ elsif payload_length_field == 127
207
+ :large
208
+ end
209
+ end
210
+
211
+ def pack_format
212
+ "#{FRAME_FORMAT[message_size]}#{actual_payload_length}"
213
+ end
214
+
215
+ def mask(data)
216
+ masked_data = ''.encode!("ASCII-8BIT")
217
+
218
+ data.each_byte.each_with_index do |byte, i|
219
+ masked_data << byte ^ mask_key.chars[i%4]
220
+ end
221
+ end
222
+
223
+ # The same algorithm applies regardless of the direction of the translation,
224
+ # e.g., the same steps are applied to mask the data as to unmask the data.
225
+ alias_method :unmask, :mask
226
+
227
+ def reset_frame!
228
+ @state = :header
229
+
230
+ @first_byte = nil
231
+ @second_byte = nil
232
+
233
+ @mask = nil
234
+
235
+ @payload_length = nil
236
+ @payload = nil
237
+ end
238
+ end
239
+ end
@@ -0,0 +1,9 @@
1
+ module WebSocket
2
+ class ServerHandshake < Http::Response
3
+
4
+ def initialize(status = nil, version = "1.1", headers = {}, body = nil, &body_proc)
5
+ @status, @version, @body, @body_proc = status, version, body, body_proc
6
+ @headers = headers
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module WebSocket
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,35 @@
1
+ require "websocket/version"
2
+ require "websocket/client_handshake"
3
+ require "websocket/server_handshake"
4
+ require "websocket/message"
5
+ require "websocket/parser"
6
+
7
+ module WebSocket
8
+ PROTOCOL_VERSION = 13 # RFC 6455
9
+ GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
10
+
11
+ # see http://tools.ietf.org/html/rfc6455#section-11.8
12
+ OPCODES = {
13
+ 0 => :continuation,
14
+ 1 => :text,
15
+ 2 => :binary,
16
+ 8 => :close,
17
+ 9 => :ping,
18
+ 10 => :pong
19
+ }
20
+
21
+ OPCODE_VALUES = {
22
+ :continuation => 0,
23
+ :text => 1,
24
+ :binary => 2,
25
+ :close => 8,
26
+ :ping => 9,
27
+ :pong => 10
28
+ }
29
+
30
+ FRAME_FORMAT = {
31
+ :small => 'CCa', # 2 bytes for header. N bytes for payload.
32
+ :medium => 'CCS<a', # 2 bytes for header. 2 bytes for extended length. N bytes for payload.
33
+ :large => 'CCQ<a' # 2 bytes for header. 4 bytes for extended length. N bytes for payload.
34
+ }
35
+ end
@@ -0,0 +1,3 @@
1
+ require 'bundler/setup'
2
+ require 'http'
3
+ require 'websocket_parser'
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe WebSocket::ClientHandshake do
4
+
5
+ let :handshake_headers do
6
+ {
7
+ "Host" => "server.example.com",
8
+ "Upgrade" => "websocket",
9
+ "Connection" => "Upgrade",
10
+ "Sec-WebSocket-Key" => "dGhlIHNhbXBsZSBub25jZQ==",
11
+ "Origin" => "http://example.com",
12
+ "Sec-WebSocket-Protocol" => "chat, superchat",
13
+ "Sec-WebSocket-Version" => "13"
14
+ }
15
+ end
16
+
17
+ let(:client_handshake) { WebSocket::ClientHandshake.new(:get, '/', handshake_headers) }
18
+
19
+ it "can validate handshake format" do
20
+ client_handshake.valid?.should be_true
21
+ end
22
+
23
+ it "can generate an accept response for the client" do
24
+ response = client_handshake.accept_response
25
+
26
+ response.status.should == 101
27
+ response.headers['Upgrade'].should == 'websocket'
28
+ response.headers['Connection'].should == 'Upgrade'
29
+ response.headers['Sec-WebSocket-Accept'].should == 's3pPLMBiTxaQ9kYGzzhZRbK+xOo='
30
+ end
31
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe WebSocket::Message do
4
+
5
+ it "knows its binary representation" do
6
+ text = 1500.times.collect { 'All work and no play makes Jack a dull boy.' }.join("\n\n")
7
+ message = WebSocket::Message.new(text)
8
+ data = message.to_data
9
+
10
+ # 2 bytes from header + 8 bytes from extended payload length + payload
11
+ data.size.should == 2 + 8 + text.length
12
+
13
+ first_byte, second_byte, ext_length, payload = data.unpack("CCQ<a#{text.length}")
14
+
15
+ first_byte.should == 0b10000001 # Text frame with FIN bit set
16
+ second_byte.should == 0b01111111 # Unmasked. Payload length 127.
17
+ ext_length.should == text.length
18
+ payload.should == text
19
+ end
20
+ end
@@ -0,0 +1,112 @@
1
+ require 'spec_helper'
2
+
3
+ describe WebSocket::Parser do
4
+
5
+ let(:received_messages) { [] }
6
+ let(:received_errors) { [] }
7
+ let(:received_closes) { [] }
8
+ let(:received_pings) { [] }
9
+ let(:received_pongs) { [] }
10
+
11
+ let(:parser) do
12
+ parser = WebSocket::Parser.new
13
+
14
+ parser.on_message { |m| received_messages << m }
15
+ parser.on_error { |m| received_errors << m }
16
+ parser.on_close { |m| received_closes << m }
17
+ parser.on_ping { |m| received_pings << m }
18
+ parser.on_pong { |m| received_pongs << m }
19
+
20
+ parser
21
+ end
22
+
23
+ it "recognizes a text message" do
24
+ parser << WebSocket::Message.new('Once upon a time').to_data
25
+
26
+ received_messages.first.should == 'Once upon a time'
27
+ end
28
+
29
+ it "can receive a message in parts" do
30
+ data = WebSocket::Message.new('Once upon a time').to_data
31
+ parser << data.slice!(0, 5)
32
+
33
+ received_messages.should be_empty
34
+
35
+ parser << data
36
+
37
+ received_messages.first.should == 'Once upon a time'
38
+ end
39
+
40
+ it "can receive succesive messages" do
41
+ msg1 = WebSocket::Message.new('Now is the winter of our discontent')
42
+ msg2 = WebSocket::Message.new('Made glorious summer by this sun of York')
43
+
44
+ parser << msg1.to_data
45
+ parser << msg2.to_data
46
+
47
+ received_messages[0].should == 'Now is the winter of our discontent'
48
+ received_messages[1].should == 'Made glorious summer by this sun of York'
49
+ end
50
+
51
+ it "can receive medium size messages" do
52
+ # Medium size messages has a payload length between 127 and 65_535 bytes
53
+
54
+ text = 4.times.collect { 'All work and no play makes Jack a dull boy.' }.join("\n\n")
55
+ text.length.should > 127
56
+ text.length.should < 65_536
57
+
58
+ parser << WebSocket::Message.new(text).to_data
59
+ received_messages.first.should == text
60
+ end
61
+
62
+ it "can receive large size messages" do
63
+ # Large size messages has a payload length greater than 65_535 bytes
64
+
65
+ text = 1500.times.collect { 'All work and no play makes Jack a dull boy.' }.join("\n\n")
66
+ text.length.should > 65_536
67
+
68
+ parser << WebSocket::Message.new(text).to_data
69
+
70
+ # Check lengths first to avoid gigantic error message
71
+ received_messages.first.length.should == text.length
72
+ received_messages.first.should == text
73
+ end
74
+
75
+ it "can receive multiframe messages" do
76
+ frame1 = WebSocket::Message.new("It is a truth universally acknowledged,", :continuation)
77
+ frame2 = WebSocket::Message.new("that a single man in possession of a good fortune,", :continuation)
78
+ frame3 = WebSocket::Message.new("must be in want of a wife.")
79
+
80
+ parser << frame1.to_data
81
+
82
+ received_messages.should be_empty
83
+
84
+ parser << frame2.to_data
85
+
86
+ received_messages.should be_empty
87
+
88
+ parser << frame3.to_data
89
+
90
+ received_messages.first.should == "It is a truth universally acknowledged," +
91
+ "that a single man in possession of a good fortune," +
92
+ "must be in want of a wife."
93
+ end
94
+
95
+ it "recognizes a ping message" do
96
+ parser << WebSocket::Message.ping('Oh, hai!').to_data
97
+
98
+ received_pings.first.should == 'Oh, hai!'
99
+ end
100
+
101
+ it "recognizes a pong message" do
102
+ parser << WebSocket::Message.pong('Hi there!').to_data
103
+
104
+ received_pongs.first.should == 'Hi there!'
105
+ end
106
+
107
+ it "recognizes a close message" do
108
+ parser << WebSocket::Message.close('Browser leaving page').to_data
109
+
110
+ received_closes.first.should == 'Browser leaving page'
111
+ end
112
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/websocket/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Alberto Fernandez-Capel"]
6
+ gem.email = ["afcapel@gmail.com"]
7
+ gem.description = %q{WebsocketParser is a RFC6455 compliant parser for websocket messages}
8
+ gem.summary = %q{Parse websockets messages in Ruby}
9
+ gem.homepage = "http://github.com/afcapel/websocket_parser"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "websocket_parser"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = WebSocket::VERSION
17
+
18
+ gem.add_runtime_dependency 'http'
19
+
20
+ gem.add_development_dependency 'rake'
21
+ gem.add_development_dependency 'rspec'
22
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: websocket_parser
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Alberto Fernandez-Capel
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: http
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: WebsocketParser is a RFC6455 compliant parser for websocket messages
63
+ email:
64
+ - afcapel@gmail.com
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .gitignore
70
+ - Gemfile
71
+ - LICENSE
72
+ - README.md
73
+ - Rakefile
74
+ - lib/websocket/client_handshake.rb
75
+ - lib/websocket/message.rb
76
+ - lib/websocket/parser.rb
77
+ - lib/websocket/server_handshake.rb
78
+ - lib/websocket/version.rb
79
+ - lib/websocket_parser.rb
80
+ - spec/spec_helper.rb
81
+ - spec/websocket/handshake_spec.rb
82
+ - spec/websocket/message_spec.rb
83
+ - spec/websocket/parser_spec.rb
84
+ - websocket_parser.gemspec
85
+ homepage: http://github.com/afcapel/websocket_parser
86
+ licenses: []
87
+ post_install_message:
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ segments:
98
+ - 0
99
+ hash: 3979494630006513518
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ segments:
107
+ - 0
108
+ hash: 3979494630006513518
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 1.8.23
112
+ signing_key:
113
+ specification_version: 3
114
+ summary: Parse websockets messages in Ruby
115
+ test_files:
116
+ - spec/spec_helper.rb
117
+ - spec/websocket/handshake_spec.rb
118
+ - spec/websocket/message_spec.rb
119
+ - spec/websocket/parser_spec.rb