libwebsocket 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,24 @@
1
+ module LibWebSocket
2
+ # Base class for all classes with states
3
+ module Stateful
4
+
5
+ attr_accessor :state
6
+
7
+ # Return true if current state match given state
8
+ def state?(val)
9
+ @state == val
10
+ end
11
+
12
+ # Change state to 'done'
13
+ def done
14
+ @state = 'done'
15
+ true
16
+ end
17
+
18
+ # Check if current state is done
19
+ def done?
20
+ @state == 'done'
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,67 @@
1
+ require 'addressable/uri'
2
+
3
+ module LibWebSocket
4
+ # Construct or parse a WebSocket URL.
5
+ class URL
6
+
7
+ attr_accessor :secure, :host, :port, :resource_name
8
+
9
+ def initialize(hash = {})
10
+ hash.each do |k,v|
11
+ instance_variable_set("@#{k}",v)
12
+ end
13
+
14
+ @secure ||= false
15
+ end
16
+
17
+ # Parse a WebSocket URL.
18
+ # @example Parse
19
+ # url = LibWebSocket::URL.new
20
+ # url.parse('wss://example.com:3000')
21
+ # url.host # => example.com
22
+ # url.port # => 3000
23
+ # url.secure # => true
24
+ def parse(string)
25
+ return nil unless string.is_a?(String)
26
+
27
+ uri = Addressable::URI.parse(string)
28
+
29
+ scheme = uri.scheme
30
+ return nil unless scheme
31
+
32
+ self.secure = true if scheme.match(/ss\Z/m)
33
+
34
+ host = uri.host
35
+ host = '/' unless host && host != ''
36
+ self.host = host
37
+ self.port = uri.port.to_s if uri.port
38
+
39
+ path = uri.path
40
+ path = '/' unless path && path != ''
41
+ self.resource_name = path
42
+
43
+ return self
44
+ end
45
+
46
+ # Construct a WebSocket URL.
47
+ # @example Construct
48
+ # url = LibWebSocket::URL.new
49
+ # url.host = 'example.com'
50
+ # url.port = '3000'
51
+ # url.secure = true
52
+ # url.to_s # => 'wss://example.com:3000'
53
+ def to_s
54
+ string = ''
55
+
56
+ string += 'ws'
57
+ string += 's' if self.secure
58
+ string += '://'
59
+ string += self.host
60
+ string += ':' + self.port.to_s if self.port
61
+ string += self.resource_name || '/'
62
+
63
+ return string
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,37 @@
1
+ require 'test_helper'
2
+
3
+ class TestCookieRequest < Test::Unit::TestCase
4
+
5
+ def test_parse
6
+ cookie = LibWebSocket::Cookie::Request.new
7
+ cookies = cookie.parse('$Version=1; foo=bar; $Path=/; $Domain=.example.com')
8
+ assert_equal 'foo', cookies[0].name
9
+ assert_equal 'bar', cookies[0].value
10
+ assert_equal 1, cookies[0].version
11
+ assert_equal '/', cookies[0].path
12
+ assert_equal '.example.com', cookies[0].domain
13
+
14
+ cookie = LibWebSocket::Cookie::Request.new
15
+ cookies = cookie.parse('$Version=1; foo=bar')
16
+ assert_equal 'foo', cookies[0].name
17
+ assert_equal 'bar', cookies[0].value
18
+ assert_equal 1, cookies[0].version
19
+ assert_equal nil, cookies[0].path
20
+ assert_equal nil, cookies[0].domain
21
+
22
+ cookie = LibWebSocket::Cookie::Request.new
23
+ cookies = cookie.parse('$Version=1; foo="hello\"there"')
24
+ assert_equal 'foo', cookies[0].name
25
+ assert_equal 'hello"there', cookies[0].value
26
+
27
+ cookie = LibWebSocket::Cookie::Request.new
28
+ cookies = cookie.parse('$Version=1; foo="bar"; $Path=/; bar=baz; $Domain=.example.com')
29
+ assert_equal 'foo', cookies[0].name
30
+ assert_equal 'bar', cookies[0].value
31
+ assert_equal '/', cookies[0].path
32
+ assert_equal 'bar', cookies[1].name
33
+ assert_equal 'baz', cookies[1].value
34
+ assert_equal '.example.com', cookies[1].domain
35
+ end
36
+
37
+ end
@@ -0,0 +1,32 @@
1
+ require 'test_helper'
2
+
3
+ class TestCookieResponse < Test::Unit::TestCase
4
+
5
+ def test_to_s
6
+ cookie = LibWebSocket::Cookie::Response.new(:name => 'foo', :value => 'bar')
7
+ assert_equal 'foo=bar; Version=1', cookie.to_s
8
+
9
+ cookie = LibWebSocket::Cookie::Response.new(
10
+ :name => 'foo',
11
+ :value => 'bar',
12
+ :discard => 1,
13
+ :max_age => 0
14
+ )
15
+ assert_equal 'foo=bar; Discard; Max-Age=0; Version=1', cookie.to_s
16
+
17
+ cookie = LibWebSocket::Cookie::Response.new(
18
+ :name => 'foo',
19
+ :value => 'bar',
20
+ :portlist => 80
21
+ )
22
+ assert_equal 'foo=bar; Port="80"; Version=1', cookie.to_s
23
+
24
+ cookie = LibWebSocket::Cookie::Response.new(
25
+ :name => 'foo',
26
+ :value => 'bar',
27
+ :portlist => [80, 443]
28
+ )
29
+ assert_equal 'foo=bar; Port="80 443"; Version=1', cookie.to_s
30
+ end
31
+
32
+ end
@@ -0,0 +1,64 @@
1
+ require 'test_helper'
2
+
3
+ class TestServerHandshake < Test::Unit::TestCase
4
+
5
+ def test_to_s
6
+ h = LibWebSocket::Handshake::Client.new
7
+ h.url = 'ws://example.com/demo'
8
+
9
+ # Mocking
10
+ h.req.key1 = "18x 6]8vM;54 *(5: { U1]8 z [ 8"
11
+ h.req.key2 = "1_ tx7X d < nw 334J702) 7]o}` 0"
12
+ h.req.challenge = "Tm[K T2u"
13
+
14
+ assert_equal h.to_s, "GET /demo HTTP/1.1\x0d\x0a" +
15
+ "Upgrade: WebSocket\x0d\x0a" +
16
+ "Connection: Upgrade\x0d\x0a" +
17
+ "Host: example.com\x0d\x0a" +
18
+ "Origin: http://example.com\x0d\x0a" +
19
+ "Sec-WebSocket-Key1: 18x 6]8vM;54 *(5: { U1]8 z [ 8\x0d\x0a" +
20
+ "Sec-WebSocket-Key2: 1_ tx7X d < nw 334J702) 7]o}` 0\x0d\x0a" +
21
+ "Content-Length: 8\x0d\x0a" +
22
+ "\x0d\x0aTm[K T2u"
23
+
24
+ h = LibWebSocket::Handshake::Client.new(:url => 'ws://example.com')
25
+
26
+ # Mocking
27
+ h.req.key1 = "18x 6]8vM;54 *(5: { U1]8 z [ 8"
28
+ h.req.key2 = "1_ tx7X d < nw 334J702) 7]o}` 0"
29
+ h.req.challenge = "Tm[K T2u"
30
+
31
+ assert_equal h.to_s, "GET / HTTP/1.1\x0d\x0a" +
32
+ "Upgrade: WebSocket\x0d\x0a" +
33
+ "Connection: Upgrade\x0d\x0a" +
34
+ "Host: example.com\x0d\x0a" +
35
+ "Origin: http://example.com\x0d\x0a" +
36
+ "Sec-WebSocket-Key1: 18x 6]8vM;54 *(5: { U1]8 z [ 8\x0d\x0a" +
37
+ "Sec-WebSocket-Key2: 1_ tx7X d < nw 334J702) 7]o}` 0\x0d\x0a" +
38
+ "Content-Length: 8\x0d\x0a" +
39
+ "\x0d\x0aTm[K T2u"
40
+
41
+ assert !h.done?
42
+ assert h.parse('')
43
+
44
+ assert h.parse("HTTP/1.1 101 WebSocket Protocol Handshake\x0d\x0a" +
45
+ "Upgrade: WebSocket\x0d\x0a" +
46
+ "Connection: Upgrade\x0d\x0a" +
47
+ "Sec-WebSocket-Origin: http://example.com\x0d\x0a" +
48
+ "Sec-WebSocket-Location: ws://example.com/demo\x0d\x0a" +
49
+ "\x0d\x0a" +
50
+ "fQJ,fN/4F4!~K~MH")
51
+ assert !h.error
52
+ assert h.done?
53
+
54
+ message = "HTTP/1.1 101 WebSocket Protocol Handshake\x0d\x0a"
55
+ h = LibWebSocket::Handshake::Client.new(:url => 'ws://example.com')
56
+ assert h.parse(message)
57
+ assert !h.error
58
+
59
+ h = LibWebSocket::Handshake::Client.new
60
+ assert !h.parse("HTTP/1.0 foo bar\x0d\x0a")
61
+ assert_equal 'Wrong response line', h.error
62
+ end
63
+
64
+ end
@@ -0,0 +1,39 @@
1
+ require 'test_helper'
2
+
3
+ class TestServerHandshake < Test::Unit::TestCase
4
+
5
+ def test_parse
6
+ h = LibWebSocket::Handshake::Server.new
7
+ assert !h.done?
8
+ assert h.parse('')
9
+
10
+ assert h.parse("GET /demo HTTP/1.1\x0d\x0a")
11
+ assert h.parse("Upgrade: WebSocket\x0d\x0a")
12
+ assert h.parse("Connection: Upgrade\x0d\x0a")
13
+ assert h.parse("Host: example.com\x0d\x0a")
14
+ assert h.parse("Origin: http://example.com\x0d\x0a")
15
+ assert h.parse("Sec-WebSocket-Key1: 18x 6]8vM;54 *(5: { U1]8 z [ 8\x0d\x0a")
16
+ assert h.parse("Sec-WebSocket-Key2: 1_ tx7X d < nw 334J702) 7]o}` 0\x0d\x0a")
17
+ assert h.parse("\x0d\x0aTm[K T2u")
18
+ assert !h.error
19
+ assert h.done?
20
+
21
+ assert_equal h.to_s, "HTTP/1.1 101 WebSocket Protocol Handshake\x0d\x0a" +
22
+ "Upgrade: WebSocket\x0d\x0a" +
23
+ "Connection: Upgrade\x0d\x0a" +
24
+ "Sec-WebSocket-Origin: http://example.com\x0d\x0a" +
25
+ "Sec-WebSocket-Location: ws://example.com/demo\x0d\x0a" +
26
+ "\x0d\x0a" +
27
+ "fQJ,fN/4F4!~K~MH";
28
+
29
+ message = "GET /demo HTTP/1.1\x0d\x0a"
30
+ h = LibWebSocket::Handshake::Server.new
31
+ assert h.parse(message)
32
+ assert_nil h.error
33
+
34
+ h = LibWebSocket::Handshake::Server.new
35
+ assert_nil h.parse("GET /demo\x0d\x0a")
36
+ assert_equal 'Wrong request line', h.error
37
+ end
38
+
39
+ end
@@ -0,0 +1,21 @@
1
+ require 'test_helper'
2
+
3
+ class TestCookie < Test::Unit::TestCase
4
+
5
+ def test_parse
6
+ cookie = LibWebSocket::Cookie.new
7
+ assert_nil cookie.parse('')
8
+ assert cookie.parse('foo=bar; baz = zab; hello= "the;re"; here')
9
+ assert_equal [['foo', 'bar'], ['baz', 'zab'], ['hello', 'the;re'], ['here', nil]], cookie.pairs
10
+ assert_equal 'foo=bar; baz=zab; hello="the;re"; here', cookie.to_s
11
+
12
+ cookie = LibWebSocket::Cookie.new
13
+ cookie.parse('$Foo="bar"')
14
+ assert_equal [['$Foo', 'bar']], cookie.pairs
15
+
16
+ cookie = LibWebSocket::Cookie.new
17
+ cookie.parse('foo=bar=123=xyz')
18
+ assert_equal [['foo', 'bar=123=xyz']], cookie.pairs
19
+ end
20
+
21
+ end
@@ -0,0 +1,65 @@
1
+ require 'test_helper'
2
+
3
+ class TestFrame < Test::Unit::TestCase
4
+
5
+ def test_append
6
+ f = LibWebSocket::Frame.new
7
+
8
+ f.append
9
+ assert_nil f.next
10
+ f.append('')
11
+ assert_nil f.next
12
+
13
+ f.append('qwe')
14
+ assert_nil f.next
15
+ f.append("\x00foo\xff")
16
+ assert_equal 'foo', f.next
17
+ assert_nil f.next
18
+
19
+ f.append("\x00")
20
+ assert_nil f.next
21
+ f.append("\xff")
22
+ assert_equal '', f.next
23
+
24
+ f.append("\x00")
25
+ assert_nil f.next
26
+ f.append("foo")
27
+ f.append("\xff")
28
+ assert_equal 'foo', f.next
29
+
30
+ f.append("\x00foo\xff\x00bar\n\xff")
31
+ assert_equal 'foo', f.next
32
+ assert_equal "bar\n", f.next
33
+ assert_nil f.next
34
+
35
+ f.append("123\x00foo\xff56\x00bar\xff789")
36
+ assert_equal 'foo', f.next
37
+ assert_equal 'bar', f.next
38
+ assert_nil f.next
39
+
40
+ # We append bytes, but read characters
41
+ msg = "\342\230\272"
42
+ f.append("\x00" + msg + "\xff")
43
+ msg.force_encoding('UTF-8') if msg.respond_to?(:force_encoding)
44
+ assert_equal msg, f.next
45
+ end
46
+
47
+ def test_new
48
+ f = LibWebSocket::Frame.new
49
+ msg = "\x00\xff"
50
+ msg.force_encoding('UTF-8') if msg.respond_to?(:force_encoding)
51
+ assert_equal msg, f.to_s
52
+
53
+ f = LibWebSocket::Frame.new('123')
54
+ msg = "\x00123\xff"
55
+ msg.force_encoding('UTF-8') if msg.respond_to?(:force_encoding)
56
+ assert_equal msg, f.to_s
57
+
58
+ # We pass characters, but send bytes
59
+ f = LibWebSocket::Frame.new("\342\230\272")
60
+ msg = "\x00\342\230\272\xff"
61
+ msg.force_encoding('UTF-8') if msg.respond_to?(:force_encoding)
62
+ assert_equal msg, f.to_s
63
+ end
64
+
65
+ end
@@ -0,0 +1,16 @@
1
+ require 'test_helper'
2
+
3
+ class TestMessage < Test::Unit::TestCase
4
+
5
+ def test_parse
6
+ m = LibWebSocket::Message.new
7
+ assert m.parse("HTTP/1.1 101 WebSocket Protocol Handshake\x0d\x0a")
8
+ assert m.parse("Upgrade: WebSocket\x0d\x0a")
9
+ assert m.parse("Connection: Upgrade\x0d\x0a")
10
+ assert m.parse("Sec-WebSocket-Origin: file://\x0d\x0a")
11
+ assert m.parse("Sec-WebSocket-Location: ws://example.com/demo\x0d\x0a")
12
+ assert m.parse("\x0d\x0a0st\x0d\x0al&q-2ZU^weu")
13
+ assert m.done?
14
+ end
15
+
16
+ end
@@ -0,0 +1,145 @@
1
+ require 'test_helper'
2
+
3
+ class TestRequest75 < Test::Unit::TestCase
4
+
5
+ def test_parse
6
+ req = LibWebSocket::Request.new
7
+ assert !req.done?
8
+ assert req.parse('')
9
+ assert req.parse("GET /demo HTTP/1.1\x0d\x0a")
10
+ assert_equal 'fields', req.state
11
+
12
+ assert req.parse("Upgrade: WebSocket\x0d\x0a")
13
+ assert_equal 'fields', req.state
14
+ assert req.parse("Connection: Upgrade\x0d\x0a")
15
+ assert_equal 'fields', req.state
16
+ assert req.parse("Host: example.com\x0d\x0a")
17
+ assert_equal 'fields', req.state
18
+ assert req.parse("Origin: http://example.com\x0d\x0a")
19
+ assert_equal 'fields', req.state
20
+ assert req.parse("\x0d\x0a")
21
+ assert_equal 'done', req.state
22
+
23
+ assert_equal 75, req.version
24
+ assert_equal '/demo', req.resource_name
25
+ assert_equal 'example.com', req.host
26
+ assert_equal 'http://example.com', req.origin
27
+
28
+ req = LibWebSocket::Request.new
29
+ assert req.parse("GET /demo HTTP/1.1\x0d\x0a")
30
+ assert req.parse("Upgrade: WebSocket\x0d\x0a")
31
+ assert req.parse("Connection: Upgrade\x0d\x0a")
32
+ assert req.parse("Host: example.com:3000\x0d\x0a")
33
+ assert req.parse("Origin: null\x0d\x0a")
34
+ assert req.parse("\x0d\x0a")
35
+ assert_equal 75, req.version
36
+ assert_equal 'done', req.state
37
+
38
+ req = LibWebSocket::Request.new
39
+ assert req.parse("GET /demo HTTP/1.1\x0d\x0a")
40
+ assert req.parse("UPGRADE: WebSocket\x0d\x0a")
41
+ assert req.parse("CONNECTION: Upgrade\x0d\x0a")
42
+ assert req.parse("HOST: example.com:3000\x0d\x0a")
43
+ assert req.parse("ORIGIN: null\x0d\x0a")
44
+ assert req.parse("\x0d\x0a")
45
+ assert_equal 75, req.version
46
+ assert_equal 'done', req.state
47
+
48
+ req = LibWebSocket::Request.new
49
+ assert req.parse("GET /demo HTTP/1.1\x0d\x0a")
50
+ assert req.parse("Upgrade: WebSocket\x0d\x0a")
51
+ assert req.parse("Connection: Upgrade\x0d\x0a")
52
+ assert req.parse("Host: example.com:3000\x0d\x0a")
53
+ assert req.parse("Origin: null\x0d\x0a")
54
+ assert req.parse("WebSocket-Protocol: sample\x0d\x0a")
55
+ assert req.parse("\x0d\x0a")
56
+ assert_equal 75, req.version
57
+ assert_equal 'done', req.state
58
+ assert_equal 'sample', req.subprotocol
59
+
60
+ req = LibWebSocket::Request.new
61
+ message = "GET /demo HTTP/1.1\x0d\x0a" +
62
+ "Upgrade: WebSocket\x0d\x0a" +
63
+ "Connection: Upgrade\x0d\x0a"
64
+ assert req.parse(message)
65
+ message = "Host: example.com:3000\x0d\x0a" + "Origin: null\x0d\x0a" + "\x0d\x0a"
66
+ assert req.parse(message)
67
+ assert_equal 75, req.version
68
+ assert req.done?
69
+
70
+ req = LibWebSocket::Request.new
71
+ assert req.parse("GET /demo HTTP/1.1\x0d\x0a")
72
+ assert req.parse("Upgrade: WebSocket\x0d\x0a")
73
+ assert req.parse("Connection: Upgrade\x0d\x0a")
74
+ assert req.parse("Host: example.com\x0d\x0a")
75
+ assert req.parse("Origin: null\x0d\x0a")
76
+ assert req.parse("cookie: \$Version=1; foo=bar; \$Path=/\x0d\x0a")
77
+ assert req.parse("\x0d\x0a")
78
+ assert req.done?
79
+
80
+ assert_equal req.cookies[0].version, '1'
81
+ assert_equal req.cookies[0].name, 'foo'
82
+ assert_equal req.cookies[0].value, 'bar'
83
+
84
+ req = LibWebSocket::Request.new
85
+ assert req.parse("GET /demo HTTP/1.1\x0d\x0a")
86
+ assert req.parse("Upgrade: WebSocket\x0d\x0a")
87
+ assert req.parse("Connection: Bar\x0d\x0a")
88
+ assert req.parse("Host: example.com\x0d\x0a")
89
+ assert req.parse("Origin: http://example.com\x0d\x0a")
90
+ assert_nil req.parse("\x0d\x0a")
91
+ assert_equal 'error', req.state
92
+ assert_equal 'Not a valid request', req.error
93
+
94
+ req = LibWebSocket::Request.new
95
+ assert req.parse("GET /demo HTTP/1.1\x0d\x0a")
96
+ assert req.parse("Upgrade: WebSocket\x0d\x0a")
97
+ assert req.parse("Connection: Upgrade\x0d\x0a")
98
+ assert req.parse("Host: example.com\x0d\x0a")
99
+ assert req.parse("Origin: http://example.com\x0d\x0a")
100
+ assert_nil req.parse("\x0d\x0afoo")
101
+ assert_equal 'error', req.state
102
+ assert_equal 'Leftovers', req.error
103
+ end
104
+
105
+ def test_to_s
106
+ req = LibWebSocket::Request.new(
107
+ :version => 75,
108
+ :host => 'example.com',
109
+ :resource_name => '/demo'
110
+ )
111
+ assert_equal req.to_s, "GET /demo HTTP/1.1\x0d\x0a" +
112
+ "Upgrade: WebSocket\x0d\x0a" +
113
+ "Connection: Upgrade\x0d\x0a" +
114
+ "Host: example.com\x0d\x0a" +
115
+ "Origin: http://example.com\x0d\x0a" +
116
+ "\x0d\x0a"
117
+
118
+ req = LibWebSocket::Request.new(
119
+ :version => 75,
120
+ :host => 'example.com',
121
+ :subprotocol => 'sample',
122
+ :resource_name => '/demo'
123
+ )
124
+ assert_equal req.to_s, "GET /demo HTTP/1.1\x0d\x0a" +
125
+ "Upgrade: WebSocket\x0d\x0a" +
126
+ "Connection: Upgrade\x0d\x0a" +
127
+ "Host: example.com\x0d\x0a" +
128
+ "Origin: http://example.com\x0d\x0a" +
129
+ "WebSocket-Protocol: sample\x0d\x0a" +
130
+ "\x0d\x0a"
131
+
132
+ req = LibWebSocket::Request.new(
133
+ :version => 75,
134
+ :host => 'example.com',
135
+ :resource_name => '/demo'
136
+ );
137
+ assert_equal req.to_s, "GET /demo HTTP/1.1\x0d\x0a" +
138
+ "Upgrade: WebSocket\x0d\x0a" +
139
+ "Connection: Upgrade\x0d\x0a" +
140
+ "Host: example.com\x0d\x0a" +
141
+ "Origin: http://example.com\x0d\x0a" +
142
+ "\x0d\x0a"
143
+ end
144
+
145
+ end