tub 0.1.0
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/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +40 -0
- data/Readme.md +48 -0
- data/lib/tub.rb +32 -0
- data/lib/tub/thin_persistent_101.rb +35 -0
- data/lib/tub/upgradable_connection.rb +56 -0
- data/lib/tub/upgradable_tcp_server.rb +72 -0
- data/lib/tub/version.rb +32 -0
- data/spec/spec_helper.rb +79 -0
- data/spec/upgradable_backend_spec.rb +73 -0
- data/tub.gemspec +26 -0
- metadata +128 -0
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm --create use default@tub > /dev/null
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
tub (0.1.0)
|
5
|
+
activesupport
|
6
|
+
thin
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: http://rubygems.org/
|
10
|
+
specs:
|
11
|
+
ZenTest (4.4.2)
|
12
|
+
activesupport (3.0.3)
|
13
|
+
autotest (4.4.6)
|
14
|
+
ZenTest (>= 4.4.1)
|
15
|
+
daemons (1.1.0)
|
16
|
+
diff-lcs (1.1.2)
|
17
|
+
eventmachine (0.12.10)
|
18
|
+
rack (1.2.1)
|
19
|
+
rspec (2.4.0)
|
20
|
+
rspec-core (~> 2.4.0)
|
21
|
+
rspec-expectations (~> 2.4.0)
|
22
|
+
rspec-mocks (~> 2.4.0)
|
23
|
+
rspec-core (2.4.0)
|
24
|
+
rspec-expectations (2.4.0)
|
25
|
+
diff-lcs (~> 1.1.2)
|
26
|
+
rspec-mocks (2.4.0)
|
27
|
+
thin (1.2.7)
|
28
|
+
daemons (>= 1.0.9)
|
29
|
+
eventmachine (>= 0.12.6)
|
30
|
+
rack (>= 1.0.0)
|
31
|
+
|
32
|
+
PLATFORMS
|
33
|
+
ruby
|
34
|
+
|
35
|
+
DEPENDENCIES
|
36
|
+
activesupport
|
37
|
+
autotest
|
38
|
+
rspec
|
39
|
+
thin
|
40
|
+
tub!
|
data/Readme.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
Try It
|
2
|
+
======
|
3
|
+
|
4
|
+
Make a server
|
5
|
+
|
6
|
+
require 'tub'
|
7
|
+
|
8
|
+
class Echo < EM::Connection
|
9
|
+
def receive_data(data)
|
10
|
+
send_data(data)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
app = lambda{|env| [200, {'Content-Type' => 'text/plain'}, ["Hello world!"]]}
|
15
|
+
|
16
|
+
Thin::Server.start('0.0.0.0', 1345, app, :backend => TUB::UpgradableTcpServer, :handler_on_upgrade => Echo)
|
17
|
+
|
18
|
+
Connect to the server
|
19
|
+
|
20
|
+
telnet localhost 1345
|
21
|
+
|
22
|
+
Speak HTTP
|
23
|
+
|
24
|
+
GET / HTTP/1.1
|
25
|
+
Host: example.com
|
26
|
+
|
27
|
+
|
28
|
+
Now switch protocols
|
29
|
+
|
30
|
+
GET / HTTP/1.1
|
31
|
+
Host: example.com
|
32
|
+
Upgrade: Echo/1.0
|
33
|
+
Connection: Upgrade
|
34
|
+
|
35
|
+
And then type anything you want, it'll echo back to you
|
36
|
+
|
37
|
+
Hello world
|
38
|
+
Hello world
|
39
|
+
|
40
|
+
Have fun!
|
41
|
+
|
42
|
+
|
43
|
+
TODO
|
44
|
+
======
|
45
|
+
* Make it actually care about what protocol the client requests and which you offer
|
46
|
+
* Figure out rackup file
|
47
|
+
* Show a sample app working in Heroku, with a sample client
|
48
|
+
* Remove the Rack wrapper app and make it part of the example, but customizable, so you control when upgrade occurs
|
data/lib/tub.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2011 Tim Shadel
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
|
24
|
+
module ThinUpgradableBackend
|
25
|
+
ROOT = File.expand_path(File.dirname(__FILE__))
|
26
|
+
end
|
27
|
+
TUB = ThinUpgradableBackend
|
28
|
+
|
29
|
+
require "#{TUB::ROOT}/tub/version"
|
30
|
+
require "#{TUB::ROOT}/tub/upgradable_connection"
|
31
|
+
require "#{TUB::ROOT}/tub/upgradable_tcp_server"
|
32
|
+
require "#{TUB::ROOT}/tub/thin_persistent_101"
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2011 Tim Shadel
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
|
24
|
+
module Thin
|
25
|
+
|
26
|
+
# This is a workaround, and should be removed as soon as Heroku deploys a Thin version
|
27
|
+
# that includes some version of https://github.com/macournoyer/thin/pull/32
|
28
|
+
class Response
|
29
|
+
alias :old_persistent? :persistent?
|
30
|
+
def persistent?
|
31
|
+
old_persistent? || (@status == 100 || @status == 101)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2011 Tim Shadel
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
|
24
|
+
require 'active_support/core_ext/class/attribute_accessors'
|
25
|
+
require 'eventmachine'
|
26
|
+
|
27
|
+
module TUB
|
28
|
+
class UpgradableConnection < EM::Connection
|
29
|
+
cattr_accessor :initial_handler_class
|
30
|
+
cattr_accessor :upgraded_handler_class
|
31
|
+
|
32
|
+
attr_accessor :handler
|
33
|
+
|
34
|
+
def post_init
|
35
|
+
@handler = UpgradableConnection.initial_handler_class.new @signature
|
36
|
+
@upgraded = false
|
37
|
+
end
|
38
|
+
|
39
|
+
def receive_data(data)
|
40
|
+
@handler.receive_data(data)
|
41
|
+
end
|
42
|
+
|
43
|
+
def unbind
|
44
|
+
@handler.unbind
|
45
|
+
end
|
46
|
+
|
47
|
+
def upgrade!
|
48
|
+
return if @upgraded
|
49
|
+
@handler = UpgradableConnection.upgraded_handler_class.new @signature
|
50
|
+
@upgraded = true
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
@@ -0,0 +1,72 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2011 Tim Shadel
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
|
24
|
+
require 'thin'
|
25
|
+
|
26
|
+
module TUB
|
27
|
+
|
28
|
+
class UpgradeApp
|
29
|
+
def initialize(conn, app)
|
30
|
+
@conn = conn
|
31
|
+
@app = app
|
32
|
+
end
|
33
|
+
|
34
|
+
def call(env)
|
35
|
+
if env["HTTP_UPGRADE"]
|
36
|
+
@conn.upgrade!
|
37
|
+
[101, { "Upgrade" => env["HTTP_UPGRADE"], "Connection" => "Upgrade" }, []]
|
38
|
+
else
|
39
|
+
@app.call(env)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class UpgradableTcpServer < Thin::Backends::TcpServer
|
45
|
+
attr_accessor :ssl
|
46
|
+
# Thin::Server assumes we'll have options
|
47
|
+
def initialize(host, port, options)
|
48
|
+
super(host, port)
|
49
|
+
UpgradableConnection.initial_handler_class = Thin::Connection
|
50
|
+
raise ":handler_on_upgrade must be provided" unless options[:handler_on_upgrade]
|
51
|
+
UpgradableConnection.upgraded_handler_class = options[:handler_on_upgrade]
|
52
|
+
end
|
53
|
+
|
54
|
+
# Connect the server
|
55
|
+
def connect
|
56
|
+
@signature = EventMachine.start_server(@host, @port, UpgradableConnection, &method(:initialize_connection))
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
# Initialize the HTTP handler when the connection is first created.
|
61
|
+
def initialize_connection(connection)
|
62
|
+
connection.handler.backend = self
|
63
|
+
ourApp = UpgradeApp.new connection, @server.app
|
64
|
+
connection.handler.app = ourApp
|
65
|
+
connection.handler.comm_inactivity_timeout = @timeout
|
66
|
+
connection.handler.threaded = @threaded
|
67
|
+
connection.handler.can_persist!
|
68
|
+
|
69
|
+
@connections << connection.handler
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/tub/version.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2011 Tim Shadel
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
|
24
|
+
module ThinUpgradableBackend
|
25
|
+
module VERSION #:nodoc:
|
26
|
+
MAJOR = 0
|
27
|
+
MINOR = 1
|
28
|
+
TINY = 0
|
29
|
+
|
30
|
+
STRING = [MAJOR, MINOR, TINY].join('.')
|
31
|
+
end
|
32
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.setup
|
3
|
+
|
4
|
+
require 'thin'
|
5
|
+
require 'tub'
|
6
|
+
|
7
|
+
unless Object.const_defined?(:DEFAULT_TEST_ADDRESS)
|
8
|
+
DEFAULT_TEST_ADDRESS = '0.0.0.0'
|
9
|
+
DEFAULT_TEST_PORT = 3333
|
10
|
+
end
|
11
|
+
|
12
|
+
module Helpers
|
13
|
+
|
14
|
+
def start_server(address=DEFAULT_TEST_ADDRESS, port=DEFAULT_TEST_PORT, options={}, &app)
|
15
|
+
@server = Thin::Server.new(address, port, options, app)
|
16
|
+
# @server.ssl = options[:ssl]
|
17
|
+
@server.threaded = options[:threaded]
|
18
|
+
@server.timeout = 3
|
19
|
+
|
20
|
+
@thread = Thread.new { @server.start }
|
21
|
+
if options[:wait_for_socket]
|
22
|
+
wait_for_socket(address, port)
|
23
|
+
else
|
24
|
+
# If we can't ping the address fallback to just wait for the server to run
|
25
|
+
sleep 1 until @server.running?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def stop_server
|
30
|
+
@server.stop!
|
31
|
+
@thread.kill
|
32
|
+
sleep 0.5
|
33
|
+
raise "Reactor still running, wtf?" if EventMachine.reactor_running?
|
34
|
+
end
|
35
|
+
|
36
|
+
def wait_for_socket(address=DEFAULT_TEST_ADDRESS, port=DEFAULT_TEST_PORT, timeout=5)
|
37
|
+
Timeout.timeout(timeout) do
|
38
|
+
loop do
|
39
|
+
begin
|
40
|
+
TCPSocket.new(address, port).close
|
41
|
+
return true
|
42
|
+
rescue
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
def send_data(data)
|
50
|
+
socket = TCPSocket.new(@server.host, @server.port)
|
51
|
+
socket.write data
|
52
|
+
out = socket.read
|
53
|
+
socket.close
|
54
|
+
out
|
55
|
+
end
|
56
|
+
|
57
|
+
def parse_response(response)
|
58
|
+
raw_headers, body = response.split("\r\n\r\n", 2)
|
59
|
+
raw_status, raw_headers = raw_headers.split("\r\n", 2)
|
60
|
+
|
61
|
+
status = raw_status.match(%r{\AHTTP/1.1\s+(\d+)\b}).captures.first.to_i
|
62
|
+
headers = Hash[ *raw_headers.split("\r\n").map { |h| h.split(/:\s+/, 2) }.flatten ]
|
63
|
+
|
64
|
+
[ status, headers, body ]
|
65
|
+
end
|
66
|
+
|
67
|
+
def get(url)
|
68
|
+
Net::HTTP.get(URI.parse("http://#{@server.host}:#{@server.port}" + url))
|
69
|
+
end
|
70
|
+
|
71
|
+
def post(url, params={})
|
72
|
+
Net::HTTP.post_form(URI.parse("http://#{@server.host}:#{@server.port}" + url), params).body
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
RSpec.configure do |config|
|
78
|
+
config.include Helpers
|
79
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Echo < EM::Connection
|
4
|
+
def receive_data(data)
|
5
|
+
send_data(data)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
describe TUB::UpgradableTcpServer do
|
11
|
+
before do
|
12
|
+
start_server DEFAULT_TEST_ADDRESS, DEFAULT_TEST_PORT, :backend => TUB::UpgradableTcpServer, :handler_on_upgrade => Echo do |env|
|
13
|
+
body = env.inspect + env['rack.input'].read
|
14
|
+
[200, { 'Content-Type' => 'text/html' }, body]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should serve HTTP from the Rack app' do
|
19
|
+
get('/?cthis').should include('cthis')
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should switch from HTTP to Echo when requested' do
|
23
|
+
socket = TCPSocket.new(@server.host, @server.port)
|
24
|
+
socket.sync = true
|
25
|
+
request = "GET / HTTP/1.1\r\nHost: arst.com\r\nUpgrade: Echo/1.0\r\nConnection: Upgrade\r\n\r\n"
|
26
|
+
|
27
|
+
socket.print request
|
28
|
+
out = ''
|
29
|
+
selection = IO.select([socket], nil, nil, 0.3)
|
30
|
+
while selection && selection[0]
|
31
|
+
begin
|
32
|
+
line = socket.readline
|
33
|
+
out << line
|
34
|
+
selection = IO.select([socket], nil, nil, 0.3)
|
35
|
+
rescue => ex
|
36
|
+
STDERR.puts ex
|
37
|
+
break
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
status, headers, body = parse_response(out)
|
42
|
+
status.should == 101
|
43
|
+
headers['Upgrade'].should == 'Echo/1.0'
|
44
|
+
headers['Connection'].should == 'Upgrade'
|
45
|
+
|
46
|
+
selection = IO.select(nil, [socket], nil, 1.5)
|
47
|
+
|
48
|
+
["I am in ", " echo", "mode"].each do |e|
|
49
|
+
socket.puts(e)
|
50
|
+
end
|
51
|
+
|
52
|
+
selection = IO.select([socket], nil, nil, 0.3)
|
53
|
+
while selection && selection[0]
|
54
|
+
begin
|
55
|
+
line = socket.readline
|
56
|
+
out << line
|
57
|
+
selection = IO.select([socket], nil, nil, 0.3)
|
58
|
+
rescue => ex
|
59
|
+
STDERR.puts ex
|
60
|
+
break
|
61
|
+
end
|
62
|
+
end
|
63
|
+
out.should match("am")
|
64
|
+
out.should match("echo")
|
65
|
+
out.should match("mode")
|
66
|
+
|
67
|
+
socket.close
|
68
|
+
end
|
69
|
+
|
70
|
+
after do
|
71
|
+
stop_server
|
72
|
+
end
|
73
|
+
end
|
data/tub.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "tub/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "tub"
|
7
|
+
s.version = ThinUpgradableBackend::VERSION::STRING
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Tim Shadel"]
|
10
|
+
s.email = ["github@timshadel.com"]
|
11
|
+
s.homepage = "http://github.com/timshadel/tub"
|
12
|
+
s.summary = %q{Make a Thin backend that can serve Rack, then easily be upgraded to a completely different EM-based protocol.}
|
13
|
+
s.description = %q{The HTTP spec details a way to use HTTP/1.1 as a platform for transitioning to newer protocols by starting the conversation with HTTP/1.1. There are no restrictions on what that protocol can be.}
|
14
|
+
|
15
|
+
# s.rubyforge_project = "tub"
|
16
|
+
|
17
|
+
s.add_development_dependency "rspec"
|
18
|
+
s.add_development_dependency "autotest"
|
19
|
+
s.add_dependency "thin"
|
20
|
+
s.add_dependency "activesupport"
|
21
|
+
|
22
|
+
s.files = `git ls-files`.split("\n")
|
23
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
24
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
25
|
+
s.require_paths = ["lib"]
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tub
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Tim Shadel
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-01-22 00:00:00 -07:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rspec
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :development
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: autotest
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 0
|
43
|
+
version: "0"
|
44
|
+
type: :development
|
45
|
+
version_requirements: *id002
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: thin
|
48
|
+
prerelease: false
|
49
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
segments:
|
55
|
+
- 0
|
56
|
+
version: "0"
|
57
|
+
type: :runtime
|
58
|
+
version_requirements: *id003
|
59
|
+
- !ruby/object:Gem::Dependency
|
60
|
+
name: activesupport
|
61
|
+
prerelease: false
|
62
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
segments:
|
68
|
+
- 0
|
69
|
+
version: "0"
|
70
|
+
type: :runtime
|
71
|
+
version_requirements: *id004
|
72
|
+
description: The HTTP spec details a way to use HTTP/1.1 as a platform for transitioning to newer protocols by starting the conversation with HTTP/1.1. There are no restrictions on what that protocol can be.
|
73
|
+
email:
|
74
|
+
- github@timshadel.com
|
75
|
+
executables: []
|
76
|
+
|
77
|
+
extensions: []
|
78
|
+
|
79
|
+
extra_rdoc_files: []
|
80
|
+
|
81
|
+
files:
|
82
|
+
- .rvmrc
|
83
|
+
- Gemfile
|
84
|
+
- Gemfile.lock
|
85
|
+
- Readme.md
|
86
|
+
- lib/tub.rb
|
87
|
+
- lib/tub/thin_persistent_101.rb
|
88
|
+
- lib/tub/upgradable_connection.rb
|
89
|
+
- lib/tub/upgradable_tcp_server.rb
|
90
|
+
- lib/tub/version.rb
|
91
|
+
- spec/spec_helper.rb
|
92
|
+
- spec/upgradable_backend_spec.rb
|
93
|
+
- tub.gemspec
|
94
|
+
has_rdoc: true
|
95
|
+
homepage: http://github.com/timshadel/tub
|
96
|
+
licenses: []
|
97
|
+
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options: []
|
100
|
+
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
segments:
|
109
|
+
- 0
|
110
|
+
version: "0"
|
111
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
|
+
none: false
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
segments:
|
117
|
+
- 0
|
118
|
+
version: "0"
|
119
|
+
requirements: []
|
120
|
+
|
121
|
+
rubyforge_project:
|
122
|
+
rubygems_version: 1.3.7
|
123
|
+
signing_key:
|
124
|
+
specification_version: 3
|
125
|
+
summary: Make a Thin backend that can serve Rack, then easily be upgraded to a completely different EM-based protocol.
|
126
|
+
test_files:
|
127
|
+
- spec/spec_helper.rb
|
128
|
+
- spec/upgradable_backend_spec.rb
|