websocket_parser 0.0.1

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.
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