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.
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if (!ARGV[0] || !ARGV[1])
4
+ puts("Usage: #{$0} secret_key hostname.com")
5
+ exit
6
+ end
7
+
8
+ require 'cloudbridge'
9
+ puts CloudBridge.generate_key(ARGV[0], ARGV[1])
@@ -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 a hash or array of hosts to listen on as the last argument.
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 arg is a hash or array, take it off and use it for initializing the cloudserver.
24
- if (args.last.kind_of?(Hash) || args.last.kind_of?(Array))
25
- listen_hosts = args.pop
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 = CloudServer.new(@host, @port)
41
+ @socket = CloudBridge::Server.new(@host, @port, listen_hosts, listen_keys)
34
42
  end
35
43
  end
36
44
  end
@@ -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
- # This class emulates the behaviour of TCPServer, but 'listens' on a cloudbridge
4
- # server rather than a local interface. Otherwise attempts to have an identical
5
- # interface to TCPServer. At least as far as mongrel uses it.
6
- class CloudServer
7
- def initialize(cloud_host, cloud_port, listen_hosts = ['*'])
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
- def setsockopt(*args)
18
- @sockopts.push(args)
19
- end
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
- def accept()
22
- # Connect to the cloudbridge and let it know we're available for a connection.
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
- socket.write("BRIDGE / HTTP/1.1\r\n")
32
- @listen_hosts.each {|host, key|
33
- socket.write("Host: #{host}" + (key ? " #{key}" : "") + "\r\n")
34
- }
35
- socket.write("\r\n")
36
- code = nil
37
- name = nil
38
- while (line = socket.gets())
39
- line = line.strip
40
- if (line == "")
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
- if (!code && !name)
57
- if (match = line.match(%r{^HTTP/1\.[01] ([0-9]{3,3}) (.*)$}))
58
- code = match[1]
59
- name = match[2]
60
- next
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
- raise "Parse error in BRIDGE request reply."
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
- def close()
69
- # doesn't need to do anything. Yet.
80
+ def close()
81
+ # doesn't need to do anything. Yet.
82
+ end
70
83
  end
71
84
  end
@@ -2,7 +2,7 @@ $: << "../lib"
2
2
 
3
3
  require "cloudserver"
4
4
 
5
- server = CloudServer.new(ARGV[0], "5432")
5
+ server = CloudBridge::Server.new(ARGV[0], "5432")
6
6
  threads = []
7
7
  ARGV[1].to_i.times {|threadid|
8
8
  threads.push Thread.new {
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.1
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