cloudbridge 0.9.1 → 0.9.2
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/bin/cloud_generate_key +9 -0
- data/bin/cloud_mongrel_rails +30 -0
- data/lib/cloud_mongrel.rb +13 -5
- data/lib/cloudbridge.rb +11 -0
- data/lib/cloudserver.rb +70 -57
- data/test/test_cloudserver.rb +1 -1
- metadata +4 -3
data/bin/cloud_mongrel_rails
CHANGED
@@ -1,5 +1,35 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require 'cloud_mongrel'
|
3
3
|
|
4
|
+
if ENV['CLOUD_HOSTS']
|
5
|
+
cloud_hosts = ENV['CLOUD_HOSTS'].split(',')
|
6
|
+
else
|
7
|
+
cloud_hosts = []
|
8
|
+
end
|
9
|
+
if ENV['CLOUD_KEYS']
|
10
|
+
cloud_keys = ENV['CLOUD_KEYS'].split(',')
|
11
|
+
else
|
12
|
+
cloud_keys = []
|
13
|
+
end
|
14
|
+
|
15
|
+
# Look for our added arguments (-H and -K)
|
16
|
+
remove = []
|
17
|
+
ARGV.each_with_index {|arg, idx|
|
18
|
+
case arg
|
19
|
+
when '-H'
|
20
|
+
cloud_hosts.push(ARGV[idx+1])
|
21
|
+
remove.push(idx)
|
22
|
+
remove.push(idx + 1)
|
23
|
+
when '-K'
|
24
|
+
cloud_keys.push(ARGV[idx+1])
|
25
|
+
remove.push(idx)
|
26
|
+
remove.push(idx +1)
|
27
|
+
end
|
28
|
+
}
|
29
|
+
remove.reverse_each {|idx| ARGV.delete_at(idx) }
|
30
|
+
|
31
|
+
ENV['CLOUD_HOSTS'] = (cloud_hosts.length() > 0 && cloud_hosts.join(',')) || nil
|
32
|
+
ENV['CLOUD_KEYS'] = (cloud_keys.length() > 0 && cloud_keys.join(',')) || nil
|
33
|
+
|
4
34
|
# just load the native mongrel version and let it do its thing with our overloaded mongrel class.
|
5
35
|
load 'mongrel_rails'
|
data/lib/cloud_mongrel.rb
CHANGED
@@ -18,19 +18,27 @@ module Mongrel
|
|
18
18
|
class HttpServer
|
19
19
|
alias_method(:native_initialize, :initialize)
|
20
20
|
|
21
|
-
# Modified to take
|
21
|
+
# Modified to take 2 arguments at the end which are a list of hosts to listen for and a list of host-keys
|
22
|
+
# to send to the server to authenticate authority over those domains.
|
22
23
|
def initialize(*args)
|
23
|
-
# If the last
|
24
|
-
|
25
|
-
|
24
|
+
# If the last two args are an array, take them off and use it for initializing the cloudserver.
|
25
|
+
listen_host_keys = args.last.kind_of?(Array) && args.pop
|
26
|
+
listen_hosts = args.last.kind_of?(Array) && args.pop
|
27
|
+
|
28
|
+
if (!listen_hosts && listen_host_keys) # if we got only one array argument at the end, make it the hosts not the keys.
|
29
|
+
listen_hosts, listen_host_keys = listen_host_keys, listen_hosts
|
26
30
|
end
|
31
|
+
# last but not least, get them from the environment or provide sensible defaults.
|
32
|
+
listen_hosts ||= (ENV['CLOUD_HOSTS'] && ENV['CLOUD_HOSTS'].split(',')) || ["*"]
|
33
|
+
listen_keys ||= (ENV['CLOUD_KEYS'] && ENV['CLOUD_KEYS'].split(',')) || []
|
34
|
+
|
27
35
|
host, port = args[0], args[1]
|
28
36
|
args[0], args[1] = '0.0.0.0', 0 # make mongrel listen on a random port, we're going to close it after.
|
29
37
|
native_initialize(*args)
|
30
38
|
@host = host
|
31
39
|
@port = port
|
32
40
|
@socket.close
|
33
|
-
@socket =
|
41
|
+
@socket = CloudBridge::Server.new(@host, @port, listen_hosts, listen_keys)
|
34
42
|
end
|
35
43
|
end
|
36
44
|
end
|
data/lib/cloudbridge.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'sha1'
|
2
|
+
|
3
|
+
# CloudBridge Server Implementation
|
4
|
+
module CloudBridge
|
5
|
+
# Generate a server key for a cloudbridge instance based on the secret key and hostname specified.
|
6
|
+
# The key grants you access to serving the host in hostname and all subdomains.
|
7
|
+
def self.generate_key(secret_key, hostname, timestamp = Time.now())
|
8
|
+
timestamp = timestamp.to_i
|
9
|
+
SHA1.hexdigest("#{secret_key}:#{timestamp}:#{hostname}") + ":#{timestamp}:#{hostname}"
|
10
|
+
end
|
11
|
+
end
|
data/lib/cloudserver.rb
CHANGED
@@ -1,71 +1,84 @@
|
|
1
1
|
require 'socket'
|
2
|
+
require 'cloudbridge'
|
2
3
|
|
3
|
-
|
4
|
-
#
|
5
|
-
#
|
6
|
-
|
7
|
-
|
8
|
-
@cloud_host = cloud_host
|
9
|
-
@cloud_port = cloud_port
|
10
|
-
@listen_hosts = {}
|
11
|
-
listen_hosts.each {|host, key|
|
12
|
-
@listen_hosts[host] = key
|
13
|
-
}
|
14
|
-
@sockopts = []
|
15
|
-
end
|
4
|
+
module CloudBridge
|
5
|
+
# This class emulates the behaviour of TCPServer, but 'listens' on a cloudbridge
|
6
|
+
# server rather than a local interface. Otherwise attempts to have an identical
|
7
|
+
# interface to TCPServer. At least as far as mongrel uses it.
|
8
|
+
class Server
|
16
9
|
|
17
|
-
|
18
|
-
|
19
|
-
|
10
|
+
def initialize(cloud_host, cloud_port, listen_hosts = ['*'], listen_keys = [])
|
11
|
+
@cloud_host = cloud_host
|
12
|
+
@cloud_port = cloud_port
|
13
|
+
@listen_hosts = listen_hosts
|
14
|
+
@listen_keys = listen_keys
|
15
|
+
@sockopts = []
|
16
|
+
end
|
20
17
|
|
21
|
-
|
22
|
-
|
23
|
-
# This is all entirely syncronous.
|
24
|
-
begin
|
25
|
-
socket = TCPSocket.new(@cloud_host, @cloud_port)
|
26
|
-
@sockopts.each {|opt| socket.setsockopt(*opt) }
|
27
|
-
rescue Errno::ECONNREFUSED
|
28
|
-
sleep(0.5)
|
29
|
-
retry
|
18
|
+
def setsockopt(*args)
|
19
|
+
@sockopts.push(args)
|
30
20
|
end
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
case code.to_i
|
42
|
-
when 100 # 100 Continue, just a ping. Ignore.
|
43
|
-
code = name = nil
|
44
|
-
next
|
45
|
-
when 101 # 101 Upgrade, successfuly got a connection.
|
46
|
-
socket.write("HTTP/1.1 100 Continue\r\n\r\n") # let the server know we're still here.
|
47
|
-
return socket
|
48
|
-
when 504 # 504 Gateway Timeout, just retry.
|
49
|
-
socket.close()
|
50
|
-
return accept()
|
51
|
-
else
|
52
|
-
raise "HTTP BRIDGE error #{code}: #{name} waiting for connection."
|
53
|
-
end
|
21
|
+
|
22
|
+
def accept()
|
23
|
+
# Connect to the cloudbridge and let it know we're available for a connection.
|
24
|
+
# This is all entirely syncronous.
|
25
|
+
begin
|
26
|
+
socket = TCPSocket.new(@cloud_host, @cloud_port)
|
27
|
+
@sockopts.each {|opt| socket.setsockopt(*opt) }
|
28
|
+
rescue Errno::ECONNREFUSED
|
29
|
+
sleep(0.5)
|
30
|
+
retry
|
54
31
|
end
|
32
|
+
socket.write("BRIDGE / HTTP/1.1\r\n")
|
33
|
+
@listen_hosts.each {|host|
|
34
|
+
socket.write("Host: #{host}\r\n")
|
35
|
+
}
|
36
|
+
@listen_keys.each {|key|
|
37
|
+
socket.write("Host-Key: #{key}\r\n")
|
38
|
+
}
|
39
|
+
socket.write("\r\n")
|
40
|
+
code = nil
|
41
|
+
name = nil
|
42
|
+
headers = []
|
43
|
+
while (line = socket.gets())
|
44
|
+
line = line.strip
|
45
|
+
if (line == "")
|
46
|
+
case code.to_i
|
47
|
+
when 100 # 100 Continue, just a ping. Ignore.
|
48
|
+
code = name = nil
|
49
|
+
headers = []
|
50
|
+
next
|
51
|
+
when 101 # 101 Upgrade, successfuly got a connection.
|
52
|
+
socket.write("HTTP/1.1 100 Continue\r\n\r\n") # let the server know we're still here.
|
53
|
+
return socket
|
54
|
+
when 504 # 504 Gateway Timeout, just retry.
|
55
|
+
socket.close()
|
56
|
+
return accept()
|
57
|
+
else
|
58
|
+
raise "HTTP BRIDGE error #{code}: #{name} waiting for connection."
|
59
|
+
end
|
60
|
+
end
|
55
61
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
62
|
+
if (!code && !name) # This is the initial response line
|
63
|
+
if (match = line.match(%r{^HTTP/1\.[01] ([0-9]{3,3}) (.*)$}))
|
64
|
+
code = match[1]
|
65
|
+
name = match[2]
|
66
|
+
next
|
67
|
+
else
|
68
|
+
raise "Parse error in BRIDGE request reply."
|
69
|
+
end
|
61
70
|
else
|
62
|
-
|
71
|
+
if (match = line.match(%r{^(.+?):\s+(.+)$}))
|
72
|
+
headers.push({match[1] => match[2]})
|
73
|
+
else
|
74
|
+
raise "Parse error in BRIDGE request reply's headers."
|
75
|
+
end
|
63
76
|
end
|
64
77
|
end
|
65
78
|
end
|
66
|
-
end
|
67
79
|
|
68
|
-
|
69
|
-
|
80
|
+
def close()
|
81
|
+
# doesn't need to do anything. Yet.
|
82
|
+
end
|
70
83
|
end
|
71
84
|
end
|
data/test/test_cloudserver.rb
CHANGED
metadata
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cloudbridge
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Graham Batty
|
8
|
-
- Kirk Haines (Swiftiply base) (?)
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
@@ -18,12 +17,14 @@ description: CloudBridge is a self-healing minimal-configuration bridge between
|
|
18
17
|
email: graham@rubyoncloud.com
|
19
18
|
executables:
|
20
19
|
- cloud_mongrel_rails
|
20
|
+
- cloud_generate_key
|
21
21
|
extensions: []
|
22
22
|
|
23
23
|
extra_rdoc_files: []
|
24
24
|
|
25
25
|
files:
|
26
26
|
- bin/cloud_mongrel_rails
|
27
|
+
- lib/cloudbridge.rb
|
27
28
|
- lib/cloud_mongrel.rb
|
28
29
|
- lib/cloudserver.rb
|
29
30
|
- test/test_cloud_mongrel.rb
|
@@ -50,7 +51,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
50
51
|
version:
|
51
52
|
requirements: []
|
52
53
|
|
53
|
-
rubyforge_project:
|
54
|
+
rubyforge_project: cloudbridge
|
54
55
|
rubygems_version: 1.2.0
|
55
56
|
signing_key:
|
56
57
|
specification_version: 2
|