thin-em-websocket-gmalette 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.
@@ -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: []