libwebsocket 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,67 @@
1
+ module LibWebSocket
2
+ # Construct or parse a WebSocket frame.
3
+ #
4
+ # SYNOPSIS
5
+ #
6
+ # # Create frame
7
+ # frame = LibWebSocket::Frame.new('123')
8
+ # frame.to_s # \x00123\xff
9
+ #
10
+ # # Parse frames
11
+ # frame = LibWebSocket::Frame.new
12
+ # frame.append("123\x00foo\xff56\x00bar\xff789")
13
+ # frame.next # => foo
14
+ # frame.next # => bar
15
+ class Frame
16
+
17
+ def initialize(buffer = nil)
18
+ @buffer = buffer || ''
19
+ end
20
+
21
+ # Create new frame without modification of current
22
+ def new(buffer = nil)
23
+ self.class.new(buffer)
24
+ end
25
+
26
+ # Append a frame chunk.
27
+ # @example
28
+ # frame.append("\x00foo")
29
+ # frame.append("bar\xff")
30
+ def append(string = nil)
31
+ return unless string.is_a?(String)
32
+
33
+ @buffer += string
34
+
35
+ return self
36
+ end
37
+
38
+ # Return the next frame.
39
+ # @example
40
+ # frame.append("\x00foo")
41
+ # frame.append("\xff\x00bar\xff")
42
+ #
43
+ # frame.next; # => foo
44
+ # frame.next; # => bar
45
+ def next
46
+ return unless @buffer.slice!(/^[^\x00]*\x00(.*?)\xff/m)
47
+
48
+ string = $1
49
+ string.force_encoding('UTF-8') if string.respond_to?(:force_encoding)
50
+
51
+ return string
52
+ end
53
+
54
+ # Construct a WebSocket frame.
55
+ # @example
56
+ # frame = LibWebSocket::Frame.new('foo')
57
+ # frame.to_s # => \x00foo\xff
58
+ def to_s
59
+ ary = ["\x00", @buffer.dup, "\xff"]
60
+
61
+ ary.collect{ |s| s.force_encoding('UTF-8') if s.respond_to?(:force_encoding) }
62
+
63
+ return ary.join
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,28 @@
1
+ module LibWebSocket
2
+ # This is a base class for LibWebSocket::Handshake::Client and LibWebSocket::Handshake::Server.
3
+ class Handshake
4
+
5
+ autoload :Client, "#{File.dirname(__FILE__)}/handshake/client"
6
+ autoload :Server, "#{File.dirname(__FILE__)}/handshake/server"
7
+
8
+ attr_accessor :secure, :error
9
+
10
+ # Convert all hash keys to instance variables.
11
+ def initialize(hash = {})
12
+ hash.each do |k,v|
13
+ instance_variable_set("@#{k}",v)
14
+ end
15
+ end
16
+
17
+ # WebSocket request object.
18
+ def req
19
+ @req ||= Request.new
20
+ end
21
+
22
+ # WebSocket response object.
23
+ def res
24
+ @res ||= Response.new
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,129 @@
1
+ module LibWebSocket
2
+ class Handshake
3
+ # Construct or parse a client WebSocket handshake. This module is written for
4
+ # convenience, since using request and response directly requires the same code
5
+ # again and again.
6
+ #
7
+ # SYNOPSIS
8
+ #
9
+ # h = LibWebSocket::Handshake::Client.new(:url => 'ws://example.com')
10
+ #
11
+ # # Create request
12
+ # h.to_s # GET /demo HTTP/1.1
13
+ # # Upgrade: WebSocket
14
+ # # Connection: Upgrade
15
+ # # Host: example.com
16
+ # # Origin: http://example.com
17
+ # # Sec-WebSocket-Key1: 18x 6]8vM;54 *(5: { U1]8 z [ 8
18
+ # # Sec-WebSocket-Key2: 1_ tx7X d < nw 334J702) 7]o}` 0
19
+ # #
20
+ # # Tm[K T2u
21
+ #
22
+ # # Parse server response
23
+ # h.parse \<<EOF
24
+ # HTTP/1.1 101 WebSocket Protocol Handshake
25
+ # Upgrade: WebSocket
26
+ # Connection: Upgrade
27
+ # Sec-WebSocket-Origin: http://example.com
28
+ # Sec-WebSocket-Location: ws://example.com/demo
29
+ #
30
+ # fQJ,fN/4F4!~K~MH
31
+ # EOF
32
+ #
33
+ # h.error # Check if there were any errors
34
+ # h.done? # Returns true
35
+ class Client < Handshake
36
+
37
+ attr_accessor :url
38
+
39
+ def initialize(hash = {})
40
+ super
41
+
42
+ self.set_url(self.url) if self.url
43
+ end
44
+
45
+ # Set or get WebSocket url.
46
+ # @see LibWebSocket::URL#initialize
47
+ # @example
48
+ # handshake.url = 'ws://example.com/demo'
49
+ def url=(val)
50
+ self.set_url(val)
51
+
52
+ return self
53
+ end
54
+
55
+ # Parse server response
56
+ # @example
57
+ # h.parse \<<EOF
58
+ # HTTP/1.1 101 WebSocket Protocol Handshake
59
+ # Upgrade: WebSocket
60
+ # Connection: Upgrade
61
+ # Sec-WebSocket-Origin: http://example.com
62
+ # Sec-WebSocket-Location: ws://example.com/demo
63
+ #
64
+ # fQJ,fN/4F4!~K~MH
65
+ # EOF
66
+ def parse(opts)
67
+ req = self.req
68
+ res = self.res
69
+
70
+ unless res.done?
71
+ unless res.parse(opts)
72
+ self.error = res.error
73
+ return
74
+ end
75
+
76
+ if res.done?
77
+ if req.version > 75 && req.checksum != res.checksum
78
+ self.error = 'Checksum is wrong.'
79
+ return
80
+ end
81
+ end
82
+ end
83
+
84
+ return true
85
+ end
86
+
87
+ # Check if response is correct
88
+ def done?
89
+ res.done?
90
+ end
91
+
92
+ # Create request
93
+ # @example
94
+ # h.to_s # GET /demo HTTP/1.1
95
+ # # Upgrade: WebSocket
96
+ # # Connection: Upgrade
97
+ # # Host: example.com
98
+ # # Origin: http://example.com
99
+ # # Sec-WebSocket-Key1: 18x 6]8vM;54 *(5: { U1]8 z [ 8
100
+ # # Sec-WebSocket-Key2: 1_ tx7X d < nw 334J702) 7]o}` 0
101
+ # #
102
+ # # Tm[K T2u
103
+ def to_s
104
+ req.to_s
105
+ end
106
+
107
+ protected
108
+
109
+ def build_url
110
+ LibWebSocket::URL.new
111
+ end
112
+
113
+ def set_url(url)
114
+ @url = self.build_url.parse(url) unless url.is_a?(LibWebSocket::URL)
115
+
116
+ req = self.req
117
+
118
+ host = @url.host
119
+ host += ':' + @url.port if @url.port
120
+ req.host = host
121
+
122
+ req.resource_name = @url.resource_name
123
+
124
+ return self
125
+ end
126
+
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,114 @@
1
+ module LibWebSocket
2
+ class Handshake
3
+ # Construct or parse a server WebSocket handshake. This module is written for
4
+ # convenience, since using request and response directly requires the same code
5
+ # again and again.
6
+ #
7
+ # SYNOPSIS
8
+ #
9
+ # h = LibWebSocket::Handshake::Server.new
10
+ #
11
+ # # Parse client request
12
+ # h.parse \<<EOF
13
+ # GET /demo HTTP/1.1
14
+ # Upgrade: WebSocket
15
+ # Connection: Upgrade
16
+ # Host: example.com
17
+ # Origin: http://example.com
18
+ # Sec-WebSocket-Key1: 18x 6]8vM;54 *(5: { U1]8 z [ 8
19
+ # Sec-WebSocket-Key2: 1_ tx7X d < nw 334J702) 7]o}` 0
20
+ #
21
+ # Tm[K T2u
22
+ # EOF
23
+ #
24
+ # h.error # Check if there were any errors
25
+ # h.idone? # Returns true
26
+ #
27
+ # # Create response
28
+ # h.to_s # HTTP/1.1 101 WebSocket Protocol Handshake
29
+ # # Upgrade: WebSocket
30
+ # # Connection: Upgrade
31
+ # # Sec-WebSocket-Origin: http://example.com
32
+ # # Sec-WebSocket-Location: ws://example.com/demo
33
+ # #
34
+ # # fQJ,fN/4F4!~K~MH
35
+ class Server < Handshake
36
+
37
+ # Parse client request
38
+ # @example
39
+ # h.parse \<<EOF
40
+ # GET /demo HTTP/1.1
41
+ # Upgrade: WebSocket
42
+ # Connection: Upgrade
43
+ # Host: example.com
44
+ # Origin: http://example.com
45
+ # Sec-WebSocket-Key1: 18x 6]8vM;54 *(5: { U1]8 z [ 8
46
+ # Sec-WebSocket-Key2: 1_ tx7X d < nw 334J702) 7]o}` 0
47
+ #
48
+ # Tm[K T2u
49
+ # EOF
50
+ def parse(opts)
51
+ req = self.req
52
+ res = self.res
53
+
54
+ unless req.done?
55
+ unless req.parse(opts)
56
+ self.error = req.error
57
+ return
58
+ end
59
+
60
+ if req.done?
61
+ res.version = req.version
62
+ res.host = req.host
63
+
64
+ # res.secure = req.secure
65
+ res.resource_name = req.resource_name
66
+ res.origin = req.origin
67
+
68
+ if req.version > 75
69
+ res.number1 = req.number1
70
+ res.number2 = req.number2
71
+ res.challenge = req.challenge
72
+ end
73
+ end
74
+ end
75
+
76
+ return true
77
+ end
78
+
79
+ # Check if request is correct and done
80
+ def done?
81
+ req.done?
82
+ end
83
+
84
+ # Create response in string format
85
+ # @example
86
+ # h.to_s # HTTP/1.1 101 WebSocket Protocol Handshake
87
+ # # Upgrade: WebSocket
88
+ # # Connection: Upgrade
89
+ # # Sec-WebSocket-Origin: http://example.com
90
+ # # Sec-WebSocket-Location: ws://example.com/demo
91
+ # #
92
+ # # fQJ,fN/4F4!~K~MH
93
+ def to_s
94
+ res.to_s
95
+ end
96
+
97
+ # Create response in rack format
98
+ # @example
99
+ # h.to_rack # [ 101,
100
+ # # {
101
+ # # 'Upgrade' => 'WebSocket'
102
+ # # 'Connection' => 'Upgrade'
103
+ # # 'Sec-WebSocket-Origin' => 'http://example.com'
104
+ # # 'Sec-WebSocket-Location' => 'ws://example.com/demo'
105
+ # # 'Content-Length' => 16
106
+ # # },
107
+ # # [ 'fQJ,fN/4F4!~K~MH' ] ]
108
+ def to_rack
109
+ res.to_rack
110
+ end
111
+
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,167 @@
1
+ require 'digest/md5'
2
+
3
+ module LibWebSocket
4
+ # A base class for LibWebSocket::Request and LibWebSocket::Response.
5
+ class Message
6
+ include Stateful
7
+
8
+ attr_accessor :fields, :error, :subprotocol, :host, :origin, :version, :number1, :number2, :challenge, :checksum
9
+
10
+ # A new instance of Message.
11
+ # Instance variables will be set from hash.
12
+ # @example
13
+ # msg = LibMessage.new( :host => 'example.org' )
14
+ # msg.host # => 'example.org'
15
+ def initialize(hash = {})
16
+ hash.each do |k,v|
17
+ instance_variable_set("@#{k}",v)
18
+ end
19
+
20
+ @version ||= 76
21
+ @buffer = ''
22
+ @fields ||= {}
23
+ @max_message_size ||= 2048
24
+ @cookies ||= []
25
+ @state = 'first_line'
26
+ end
27
+
28
+ # Alias for fields.
29
+ # Use this instead of fields directly to preserve normalization(lowercase etc.)
30
+ def field(f, val = nil)
31
+ name = f.downcase
32
+
33
+ return self.fields[name] unless val
34
+
35
+ self.fields[name] = val
36
+
37
+ return self
38
+ end
39
+
40
+ # Set state to 'error' and write error message
41
+ def error=(val)
42
+ @error = val
43
+ self.state = 'error'
44
+ end
45
+
46
+ # Calculate Draft 76 checksum
47
+ def checksum
48
+ return @checksum if @checksum
49
+
50
+ raise 'number1 is required' unless self.number1
51
+ raise 'number2 is required' unless self.number2
52
+ raise 'challenge is required' unless self.challenge
53
+
54
+ checksum = ''
55
+ checksum += [self.number1].pack('N')
56
+ checksum += [self.number2].pack('N')
57
+ checksum += self.challenge
58
+ checksum = Digest::MD5.digest(checksum)
59
+
60
+ return @checksum ||= checksum
61
+ end
62
+
63
+ # Parse string
64
+ # @see LibWebSocket::Request#parse
65
+ # @see LibWebSocket::Response#parse
66
+ def parse(string)
67
+ return unless string.is_a?(String)
68
+
69
+ return if self.error
70
+
71
+ return unless self.append(string)
72
+
73
+ while(!self.state?('body') && line = self.get_line)
74
+ if self.state?('first_line')
75
+ return unless self.parse_first_line(line)
76
+
77
+ self.state = 'fields'
78
+ elsif line != ''
79
+ return unless self.parse_field(line)
80
+ else
81
+ self.state = 'body'
82
+ break
83
+ end
84
+ end
85
+
86
+ return true unless self.state?('body')
87
+
88
+ rv = self.parse_body
89
+ return unless rv
90
+
91
+ # Need more data
92
+ return rv unless rv != true
93
+
94
+ return self.done
95
+ end
96
+
97
+ protected
98
+
99
+ def number(name, key, value = nil)
100
+ if value
101
+ return self.instance_variable_set("@#{name}", value)
102
+ end
103
+
104
+ return self.instance_variable_get("@#{name}") if self.instance_variable_get("@#{name}")
105
+
106
+ return self.instance_variable_set("@#{name}", self.extract_number(self.send(key)))
107
+ end
108
+
109
+ def extract_number(key)
110
+ number = ''
111
+ while key.slice!(/(\d)/)
112
+ number += $1
113
+ end
114
+ number = number.to_i
115
+
116
+ spaces = 0
117
+ while key.slice!(/ /)
118
+ spaces += 1
119
+ end
120
+
121
+ return if spaces == 0
122
+
123
+ return (number / spaces).to_i
124
+ end
125
+
126
+ def append(data)
127
+ return if self.error
128
+
129
+ @buffer += data
130
+
131
+ if @buffer.length > @max_message_size
132
+ self.error = 'Message is too long'
133
+ return
134
+ end
135
+
136
+ return self
137
+ end
138
+
139
+ def get_line
140
+ if @buffer.slice!(/\A(.*?)\x0d?\x0a/)
141
+ return $1
142
+ end
143
+ return
144
+ end
145
+
146
+ def parse_first_line(line)
147
+ self
148
+ end
149
+
150
+ def parse_field(line)
151
+ name, value = line.split(': ', 2)
152
+ unless name && value
153
+ self.error = 'Invalid field'
154
+ return
155
+ end
156
+
157
+ self.field(name, value)
158
+
159
+ return self
160
+ end
161
+
162
+ def parse_body
163
+ self
164
+ end
165
+
166
+ end
167
+ end