thin-em-websocket-gmalette 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3e56114ee23b9f4aba27e061eb7f9c850c225510
4
+ data.tar.gz: f15eb765178e22d51559af04e72f6cdd5c8bb51f
5
+ SHA512:
6
+ metadata.gz: 945be7f0b09249e287eed6be7adeaca7b6f0f6165f6397a1c5d21f0f752e0a20993bd1e308cfb107291b559df194590f3d6d1ffd57fa7ceb045275273d158517
7
+ data.tar.gz: 1b94b5351db6402683a36ee0280f967bf4e1e4e1c6d7da99501a95e056802298393325d65c98dc9ed56ba2ea06c549b85e5f25e56263849e449d5f8bd125c0a9
@@ -0,0 +1,18 @@
1
+ *.swp
2
+ *.gem
3
+ *.rbc
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in thin-em-websocket.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ Copyright (c) 2012 Sam Saffron
2
+
3
+ portions from em-websocket
4
+ Copyright (c) 2009 Ilya Grigorik
5
+
6
+
7
+ MIT License
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining
10
+ a copy of this software and associated documentation files (the
11
+ "Software"), to deal in the Software without restriction, including
12
+ without limitation the rights to use, copy, modify, merge, publish,
13
+ distribute, sublicense, and/or sell copies of the Software, and to
14
+ permit persons to whom the Software is furnished to do so, subject to
15
+ the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be
18
+ included in all copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ # Thin::Em::Websocket
2
+
3
+ Thin support for the em-websocket gem
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'thin-em-websocket'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install thin-em-websocket
18
+
19
+ ## Usage
20
+
21
+
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1 @@
1
+ gem 'thin-em-websocket', :path => '../'
@@ -0,0 +1,38 @@
1
+ require 'thin-em-websocket'
2
+
3
+ echo = lambda do |env|
4
+ connection = env['em.connection']
5
+ if connection && connection.websocket?
6
+ puts "upgrading web socket"
7
+ begin
8
+ connection.upgrade_websocket
9
+ rescue => e
10
+ p "#{e}"
11
+ e.backtrace.each do |b|
12
+ p b
13
+ end
14
+ end
15
+ connection.onmessage { |m|
16
+ puts "GOT #{m}"
17
+ connection.send(m)
18
+ puts "SEND #{m}"
19
+ }
20
+ return Thin::Connection::AsyncResponse
21
+ end
22
+
23
+ [200, {"Content-Type" => "text/html"}, [<<-HTML
24
+ <html>
25
+ <body>
26
+ <script>
27
+ socket = new WebSocket("ws://" + document.location.hostname + ":" + (document.location.port || 80) + "/ws");
28
+ socket.onerror = function(m){ alert("error: " + m); };
29
+ socket.onopen = function() { alert("connected"); socket.send("hello world") };
30
+ socket.onmessage = function(msg) { alert(msg.data); }
31
+ </script>
32
+ </body>
33
+ </html>
34
+ HTML
35
+ ]]
36
+ end
37
+
38
+ run echo
@@ -0,0 +1,282 @@
1
+ # much of the code here is lifted from https://github.com/igrigorik/em-websocket/blob/master/lib/em-websocket/connection.rb
2
+
3
+ require "thin-em-websocket/version"
4
+ require "em-websocket"
5
+
6
+ module ThinEM
7
+ module Websocket
8
+ end
9
+ end
10
+
11
+ # connection SHIM, so we only override minimal amounts of thin
12
+ class ThinEM::Websocket::Connection
13
+ attr_writer :max_frame_size
14
+
15
+ # define WebSocket callbacks
16
+ def onopen(&blk); @onopen = blk; end
17
+ def onclose(&blk); @onclose = blk; end
18
+ def onerror(&blk); @onerror = blk; end
19
+ def onmessage(&blk); @onmessage = blk; end
20
+ def onping(&blk); @onping = blk; end
21
+ def onpong(&blk); @onpong = blk; end
22
+
23
+ def trigger_on_message(msg)
24
+ @onmessage.call(msg) if @onmessage
25
+ end
26
+ def trigger_on_open
27
+ @onopen.call if @onopen
28
+ end
29
+ def trigger_on_close
30
+ @onclose.call if @onclose
31
+ end
32
+ def trigger_on_ping(data)
33
+ @onping.call(data) if @onping
34
+ end
35
+ def trigger_on_pong(data)
36
+ @onpong.call(data) if @onpong
37
+ end
38
+ def trigger_on_error(reason)
39
+ return false unless @onerror
40
+ @onerror.call(reason)
41
+ true
42
+ end
43
+
44
+ def initialize(connection)
45
+ @connection = connection
46
+ @logger = Class.new do
47
+ def error(m); end
48
+ def warn(m); end
49
+ def debug(m); end
50
+ def info(m); end
51
+ end.new
52
+ end
53
+
54
+ def logger=(logger)
55
+ @logger = logger
56
+ end
57
+
58
+ def websocket?
59
+ true
60
+ end
61
+
62
+ def upgrade_websocket
63
+ return if @handler
64
+ @handler = EM::WebSocket::HandlerFactory.build(self, @connection.ws_buffer, false, nil)
65
+ unless @handler
66
+ # see: https://github.com/learnboost/socket.io/commit/9982232032771574ceb68e2bccee4e43fd5af887#diff-0
67
+ # hixie-76 behind HAProxy gets a bit messy, we need to send the header first to unblock the stream
68
+ if !@sent_upgrade && @connection.ws_buffer =~ /sec-websocket-key1/i
69
+ @logger.info("WebSocket: attempting hixie 76 hack")
70
+
71
+ fake_buffer = @connection.ws_buffer.dup
72
+ fake_buffer << "12345678"
73
+ (header, remains) = fake_buffer.split("\r\n\r\n", 2)
74
+ fake_handler = EM::WebSocket::HandlerFactory.build(self, fake_buffer, false, nil)
75
+
76
+ @handshake_76_without_verify = fake_handler.handshake[0..-17]
77
+ send_data(@handshake_76_without_verify)
78
+ @sent_upgrade = true
79
+ end
80
+ end
81
+ @connection.set_comm_inactivity_timeout(0) if @handler
82
+ @handler.run if @handler
83
+ end
84
+
85
+ def upgraded?
86
+ !@handler.nil?
87
+ end
88
+
89
+ def pending_upgrade?
90
+ @handler.nil? && @sent_upgrade
91
+ end
92
+
93
+ # Cache encodings since it's moderately expensive to look them up each time
94
+ ENCODING_SUPPORTED = "string".respond_to?(:force_encoding)
95
+ UTF8 = Encoding.find("UTF-8") if ENCODING_SUPPORTED
96
+ BINARY = Encoding.find("BINARY") if ENCODING_SUPPORTED
97
+
98
+
99
+ def send_data(data)
100
+ if @sent_upgrade && !@upgrade_stripped
101
+ # strip it
102
+ raise EventMachine::WebSocket::HandshakeError if @handshake_76_without_verify != data[0..@handshake_76_without_verify.length-1]
103
+ data = data[@handshake_76_without_verify.length..-1]
104
+ @upgrade_stripped = true
105
+ end
106
+ @connection.send_data(data)
107
+ end
108
+
109
+ def receive_data(data)
110
+ begin
111
+ @logger.info("Got Socket Data (l: #{data.length})")
112
+ @handler.receive_data(data)
113
+ rescue EventMachine::WebSocket::HandshakeError => e
114
+ @logger.warn("Web Socket Failed to handshake")
115
+ trigger_on_error(e)
116
+ # Errors during the handshake require the connection to be aborted
117
+ abort
118
+ rescue EventMachine::WebSocket::WSProtocolError => e
119
+ @logger.warn("Web Socket Protocol Error")
120
+ trigger_on_error(e)
121
+ close_websocket_private(e.code)
122
+ rescue => e
123
+ # These are application errors - raise unless onerror defined
124
+ trigger_on_error(e) || raise(e)
125
+ # There is no code defined for application errors, so use 3000
126
+ # (which is reserved for frameworks)
127
+ close_websocket_private(3000)
128
+ end
129
+ end
130
+
131
+ # Send a WebSocket text frame.
132
+ #
133
+ # A WebSocketError may be raised if the connection is in an opening or a
134
+ # closing state, or if the passed in data is not valid UTF-8
135
+ #
136
+ def send(data)
137
+ # If we're using Ruby 1.9, be pedantic about encodings
138
+ if ENCODING_SUPPORTED
139
+ # Also accept ascii only data in other encodings for convenience
140
+ unless (data.encoding == UTF8 && data.valid_encoding?) || data.ascii_only?
141
+ raise WebSocketError, "Data sent to WebSocket must be valid UTF-8 but was #{data.encoding} (valid: #{data.valid_encoding?})"
142
+ end
143
+ # This labels the encoding as binary so that it can be combined with
144
+ # the BINARY framing
145
+ data.force_encoding(BINARY)
146
+ else
147
+ # TODO: Check that data is valid UTF-8
148
+ end
149
+
150
+ if @handler
151
+ @handler.send_text_frame(data)
152
+ else
153
+ raise WebSocketError, "Cannot send data before onopen callback"
154
+ end
155
+
156
+ # Revert data back to the original encoding (which we assume is UTF-8)
157
+ # Doing this to avoid duping the string - there may be a better way
158
+ data.force_encoding(UTF8) if ENCODING_SUPPORTED
159
+ return nil
160
+ end
161
+
162
+ # Send a ping to the client. The client must respond with a pong.
163
+ #
164
+ # In the case that the client is running a WebSocket draft < 01, false
165
+ # is returned since ping & pong are not supported
166
+ #
167
+ def ping(body = '')
168
+ if @handler
169
+ @handler.pingable? ? @handler.send_frame(:ping, body) && true : false
170
+ else
171
+ raise WebSocketError, "Cannot ping before onopen callback"
172
+ end
173
+ end
174
+
175
+ # Send an unsolicited pong message, as allowed by the protocol. The
176
+ # client is not expected to respond to this message.
177
+ #
178
+ # em-websocket automatically takes care of sending pong replies to
179
+ # incoming ping messages, as the protocol demands.
180
+ #
181
+ def pong(body = '')
182
+ if @handler
183
+ @handler.pingable? ? @handler.send_frame(:pong, body) && true : false
184
+ else
185
+ raise WebSocketError, "Cannot ping before onopen callback"
186
+ end
187
+ end
188
+
189
+ # Test whether the connection is pingable (i.e. the WebSocket draft in
190
+ # use is >= 01)
191
+ def pingable?
192
+ if @handler
193
+ @handler.pingable?
194
+ else
195
+ raise WebSocketError, "Cannot test whether pingable before onopen callback"
196
+ end
197
+ end
198
+
199
+
200
+ def state
201
+ @handler ? @handler.state : :handshake
202
+ end
203
+
204
+ # Returns the maximum frame size which this connection is configured to
205
+ # accept. This can be set globally or on a per connection basis, and
206
+ # defaults to a value of 10MB if not set.
207
+ #
208
+ # The behaviour when a too large frame is received varies by protocol,
209
+ # but in the newest protocols the connection will be closed with the
210
+ # correct close code (1009) immediately after receiving the frame header
211
+ #
212
+ def max_frame_size
213
+ @max_frame_size || EventMachine::WebSocket.max_frame_size
214
+ end
215
+
216
+ def close_connection_after_writing()
217
+ @connection.close_connection_after_writing()
218
+ end
219
+
220
+ private
221
+
222
+ # As definited in draft 06 7.2.2, some failures require that the server
223
+ # abort the websocket connection rather than close cleanly
224
+ def abort
225
+ @connection.close_connection
226
+ end
227
+
228
+ def close_websocket_private(code, body = nil)
229
+ if @handler
230
+ @handler.close_websocket(code, body)
231
+ else
232
+ # The handshake hasn't completed - should be safe to terminate
233
+ abort
234
+ end
235
+ end
236
+
237
+ end
238
+
239
+ class Thin::Connection
240
+ # based off https://github.com/faye/faye-websocket-ruby/blob/master/lib/faye/adapters/thin.rb
241
+ # and code in em-websocket
242
+
243
+ alias :thin_process :process
244
+ alias :thin_receive_data :receive_data
245
+
246
+ attr_reader :ws_buffer
247
+
248
+
249
+ def process
250
+ if websocket? && !@request.env['em.connection']
251
+ @socket_connection = ThinEM::Websocket::Connection.new(self)
252
+ @request.env['em.connection'] = @socket_connection
253
+ @response.persistent!
254
+ end
255
+ thin_process
256
+ end
257
+
258
+ def receive_data(data)
259
+ if @socket_connection && @socket_connection.upgraded?
260
+ @socket_connection.receive_data(data)
261
+ else
262
+ @ws_buffer ||= ""
263
+ @ws_buffer << data unless @ws_buffer == false
264
+ @ws_buffer = false if @ws_buffer.length > 10000 # some sane cutoff so we dont have too much data in memory
265
+ @socket_connection.upgrade_websocket if @socket_connection && @socket_connection.pending_upgrade?
266
+ thin_receive_data(data)
267
+ end
268
+
269
+ end
270
+
271
+ def websocket?
272
+ return @websocket unless @websocket == nil
273
+ env = @request.env
274
+ @websocket =
275
+ env['REQUEST_METHOD'] == 'GET' and
276
+ env['HTTP_CONNECTION'] and
277
+ env['HTTP_CONNECTION'].split(/\s*,\s*/).include?('Upgrade') and
278
+ env['HTTP_UPGRADE'].downcase == 'websocket'
279
+ end
280
+
281
+
282
+ end
@@ -0,0 +1,5 @@
1
+ module ThinEM
2
+ module Websocket
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/thin-em-websocket/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Sam Saffron"]
6
+ gem.email = ["sam.saffron@gmail.com"]
7
+ gem.description = %q{thin support for em-websocket}
8
+ gem.summary = %q{thin support for em-websocket}
9
+ gem.homepage = ""
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 = "thin-em-websocket-gmalette"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = ThinEM::Websocket::VERSION
17
+ gem.add_dependency('em-websocket', '>= 0.3.8')
18
+ gem.add_dependency('thin', '>= 1.4.1')
19
+
20
+ gem.add_development_dependency('rake')
21
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: thin-em-websocket-gmalette
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Sam Saffron
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: em-websocket
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.3.8
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.3.8
27
+ - !ruby/object:Gem::Dependency
28
+ name: thin
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.4.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 1.4.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: thin support for em-websocket
56
+ email:
57
+ - sam.saffron@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - LICENSE
65
+ - README.md
66
+ - Rakefile
67
+ - examples/Gemfile
68
+ - examples/config.ru
69
+ - lib/thin-em-websocket.rb
70
+ - lib/thin-em-websocket/version.rb
71
+ - thin-em-websocket.gemspec
72
+ homepage: ''
73
+ licenses: []
74
+ metadata: {}
75
+ post_install_message:
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubyforge_project:
91
+ rubygems_version: 2.2.2
92
+ signing_key:
93
+ specification_version: 4
94
+ summary: thin support for em-websocket
95
+ test_files: []