caldecott 0.0.1 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,3 +1,2 @@
1
1
  # caldecott
2
-
3
- TBD
2
+ TCP over HTTP tunnel.
data/lib/caldecott.rb ADDED
@@ -0,0 +1,11 @@
1
+ # Copyright (c) 2009-2011 VMware, Inc.
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+
6
+ require 'caldecott/client'
7
+ require 'caldecott/server'
8
+
9
+ require 'caldecott/session_logger'
10
+ require 'caldecott/tcp_connection'
11
+ require 'caldecott/version'
@@ -0,0 +1,7 @@
1
+ # Copyright (c) 2009-2011 VMware, Inc.
2
+
3
+ require 'caldecott'
4
+ require 'caldecott/client/client'
5
+ require 'caldecott/client/tunnel'
6
+ require 'caldecott/client/http_tunnel'
7
+ require 'caldecott/client/websocket_tunnel'
@@ -0,0 +1,79 @@
1
+ # Copyright (c) 2009-2011 VMware, Inc.
2
+
3
+ require 'eventmachine'
4
+
5
+ module Caldecott
6
+ module Client
7
+ def self.sanitize_url(tun_url)
8
+ tun_url = tun_url =~ /(http|https|ws).*/i ? tun_url : "https://#{tun_url}"
9
+ end
10
+
11
+ def self.start(opts)
12
+ local_port = opts[:local_port]
13
+ tun_url = opts[:tun_url]
14
+ dst_host = opts[:dst_host]
15
+ dst_port = opts[:dst_port]
16
+ log_file = opts[:log_file]
17
+ log_level = opts[:log_level]
18
+ auth_token = opts[:auth_token]
19
+
20
+ @quiet = opts[:quiet]
21
+
22
+ trap("TERM") { stop }
23
+ trap("INT") { stop }
24
+
25
+ tun_url = sanitize_url(tun_url)
26
+
27
+ EM.run do
28
+ unless @quiet
29
+ puts "Starting local server on port #{local_port} to #{tun_url}"
30
+ end
31
+
32
+ EM.start_server("localhost", local_port, TcpConnection) do |conn|
33
+ # avoid races between tunnel setup and incoming local data
34
+ conn.pause
35
+
36
+ log = SessionLogger.new("client", log_file)
37
+ log.level = SessionLogger.severity_from_string(log_level)
38
+
39
+ tun = nil
40
+
41
+ conn.onopen do
42
+ log.debug "local connected"
43
+ tun = Tunnel.start(log, tun_url, dst_host, dst_port, auth_token)
44
+ end
45
+
46
+ tun.onopen do
47
+ log.debug "tunnel connected"
48
+ conn.resume
49
+ end
50
+
51
+ conn.onreceive do |data|
52
+ log.debug "l -> t #{data.length}"
53
+ tun.send_data(data)
54
+ end
55
+
56
+ tun.onreceive do |data|
57
+ log.debug("l <- t #{data.length}")
58
+ conn.send_data(data)
59
+ end
60
+
61
+ conn.onclose do
62
+ log.debug "local closed"
63
+ tun.close
64
+ end
65
+
66
+ tun.onclose do
67
+ log.debug "tunnel closed"
68
+ conn.close_connection_after_writing
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ def self.stop
75
+ puts "Caldecott shutting down" unless @quiet
76
+ EM.stop
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,236 @@
1
+ # Copyright (c) 2009-2011 VMware, Inc.
2
+
3
+ require 'em-http'
4
+ require 'json'
5
+
6
+ module Caldecott
7
+ module Client
8
+ class HttpTunnel
9
+ MAX_RETRIES = 10
10
+
11
+ def initialize(logger, url, dst_host, dst_port, auth_token)
12
+ @log, @auth_token = logger, auth_token
13
+ @closing = false
14
+ @retries = 0
15
+ init_msg = ""
16
+
17
+ # FIXME: why is this optional?
18
+ if dst_host
19
+ init_msg = { :host => dst_host, :port => dst_port }.to_json
20
+ end
21
+
22
+ start(url, init_msg)
23
+ end
24
+
25
+ def onopen(&blk)
26
+ @onopen = blk
27
+ @onopen.call if @opened
28
+ end
29
+
30
+ def onclose(&blk)
31
+ @onclose = blk
32
+ @onclose.call if @closed
33
+ end
34
+
35
+ def onreceive(&blk)
36
+ @onreceive = blk
37
+ end
38
+
39
+ def send_data(data)
40
+ @writer.send_data(data)
41
+ end
42
+
43
+ def close
44
+ return if @closing or @closed
45
+ @closing = true
46
+ @writer.close if @writer
47
+ @reader.close if @reader
48
+ stop
49
+ end
50
+
51
+ def trigger_on_open
52
+ @opened = true
53
+ @onopen.call if @onopen
54
+ end
55
+
56
+ def trigger_on_close
57
+ close
58
+ @closed = true
59
+ @onclose.call if @onclose
60
+ @onclose = nil
61
+ end
62
+
63
+ def trigger_on_receive(data)
64
+ @onreceive.call(data)
65
+ end
66
+
67
+ def start(base_uri, init_msg)
68
+ if (@retries += 1) > MAX_RETRIES
69
+ trigger_on_close
70
+ return
71
+ end
72
+
73
+ begin
74
+ parsed_uri = Addressable::URI.parse(base_uri)
75
+ parsed_uri.path = '/tunnels'
76
+
77
+ @log.debug "post #{parsed_uri.to_s}"
78
+ req = EM::HttpRequest.new(parsed_uri.to_s).post :body => init_msg, :head => { "Auth-Token" => @auth_token }
79
+
80
+ req.callback do
81
+ @log.debug "post #{parsed_uri.to_s} #{req.response_header.status}"
82
+ unless [200, 201, 204].include?(req.response_header.status)
83
+ start(base_uri, init_msg)
84
+ else
85
+ @retries = 0
86
+ resp = JSON.parse(req.response)
87
+
88
+ parsed_uri.path = resp["path"]
89
+ @tun_uri = parsed_uri.to_s
90
+
91
+ parsed_uri.path = resp["path_out"]
92
+ @reader = Reader.new(@log, parsed_uri.to_s, self, @auth_token)
93
+
94
+ parsed_uri.path = resp["path_in"]
95
+ @writer = Writer.new(@log, parsed_uri.to_s, self, @auth_token)
96
+ trigger_on_open
97
+ end
98
+ end
99
+
100
+ req.errback do
101
+ @log.debug "post #{parsed_uri.to_s} error"
102
+ start(base_uri, init_msg)
103
+ end
104
+
105
+ rescue Exception => e
106
+ @log.error e
107
+ trigger_on_close
108
+ raise e
109
+ end
110
+ end
111
+
112
+ def stop
113
+ if (@retries += 1) > MAX_RETRIES
114
+ trigger_on_close
115
+ return
116
+ end
117
+
118
+ return if @tun_uri.nil?
119
+
120
+ @log.debug "delete #{@tun_uri}"
121
+ req = EM::HttpRequest.new("#{@tun_uri}").delete :head => { "Auth-Token" => @auth_token }
122
+
123
+ req.errback do
124
+ @log.debug "delete #{@tun_uri} error"
125
+ stop
126
+ end
127
+
128
+ req.callback do
129
+ @log.debug "delete #{@tun_uri} #{req.response_header.status}"
130
+ if [200, 202, 204, 404].include?(req.response_header.status)
131
+ trigger_on_close
132
+ else
133
+ stop
134
+ end
135
+ end
136
+ end
137
+
138
+ class Reader
139
+ def initialize(log, uri, conn, auth_token)
140
+ @log, @base_uri, @conn, @auth_token = log, uri, conn, auth_token
141
+ @retries = 0
142
+ @closing = false
143
+ start
144
+ end
145
+
146
+ def close
147
+ @closing = true
148
+ end
149
+
150
+ def start(seq = 1)
151
+ if (@retries += 1) > MAX_RETRIES
152
+ @conn.trigger_on_close
153
+ return
154
+ end
155
+
156
+ return if @closing
157
+ uri = "#{@base_uri}/#{seq}"
158
+ @log.debug "get #{uri}"
159
+ req = EM::HttpRequest.new(uri).get :timeout => 0, :head => { "Auth-Token" => @auth_token }
160
+
161
+ req.errback do
162
+ @log.debug "get #{uri} error"
163
+ start(seq)
164
+ end
165
+
166
+ req.callback do
167
+ @log.debug "get #{uri} #{req.response_header.status}"
168
+ case req.response_header.status
169
+ when 200
170
+ @conn.trigger_on_receive(req.response)
171
+ @retries = 0
172
+ start(seq + 1)
173
+ when 404
174
+ @conn.trigger_on_close
175
+ else
176
+ start(seq)
177
+ end
178
+ end
179
+ end
180
+ end
181
+
182
+ class Writer
183
+ def initialize(log, uri, conn, auth_token)
184
+ @log, @uri, @conn, @auth_token = log, uri, conn, auth_token
185
+ @retries = 0
186
+ @seq, @write_buffer = 1, ""
187
+ @closing = @writing = false
188
+ end
189
+
190
+ def send_data(data)
191
+ @write_buffer << data
192
+ send_data_buffered
193
+ end
194
+
195
+ def close
196
+ @closing = true
197
+ end
198
+
199
+ def send_data_buffered
200
+ if (@retries += 1) > MAX_RETRIES
201
+ @conn.trigger_on_close
202
+ return
203
+ end
204
+
205
+ return if @closing
206
+ data, @write_buffer = @write_buffer, "" unless @writing
207
+
208
+ @writing = true
209
+ uri = "#{@uri}/#{@seq}"
210
+ @log.debug "put #{uri}"
211
+ req = EM::HttpRequest.new(uri).put :body => data, :head => { "Auth-Token" => @auth_token }
212
+
213
+ req.errback do
214
+ @log.debug "put #{uri} error"
215
+ send_data_buffered
216
+ end
217
+
218
+ req.callback do
219
+ @log.debug "put #{uri} #{req.response_header.status}"
220
+ case req.response_header.status
221
+ when 200, 202, 204
222
+ @writing = false
223
+ @seq += 1
224
+ @retries = 0
225
+ send_data_buffered unless @write_buffer.empty?
226
+ when 404
227
+ @conn.trigger_on_close
228
+ else
229
+ send_data_buffered
230
+ end
231
+ end
232
+ end
233
+ end
234
+ end
235
+ end
236
+ end
@@ -0,0 +1,23 @@
1
+ # Copyright (c) 2009-2011 VMware, Inc.
2
+
3
+ require 'addressable/uri'
4
+
5
+ module Caldecott
6
+ module Client
7
+ module Tunnel
8
+ # Note: I wanted to do this with self#new but had issues
9
+ # with getting send :initialize to figure out the right
10
+ # number of arguments
11
+ def self.start(logger, tun_url, dst_host, dst_port, auth_token)
12
+ case Addressable::URI.parse(tun_url).normalized_scheme
13
+ when "http", "https"
14
+ HttpTunnel.new(logger, tun_url, dst_host, dst_port, auth_token)
15
+ when "ws"
16
+ WebSocketTunnel.new(logger, tun_url, dst_host, dst_port, auth_token)
17
+ else
18
+ raise "invalid url"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,36 @@
1
+ # Copyright (c) 2009-2011 VMware, Inc.
2
+
3
+ require 'em-http'
4
+
5
+ module Caldecott
6
+ module Client
7
+ class WebSocketTunnel
8
+ def initialize(logger, url, dst_host, dst_port, auth_token)
9
+ @ws = EM::HttpRequest.new("#{url}/websocket/#{dst_host}/#{dst_port}").get :timeout => 0
10
+ end
11
+
12
+ def onopen(&blk)
13
+ @ws.callback { blk.call }
14
+ end
15
+
16
+ def onclose(&blk)
17
+ @ws.errback { blk.call }
18
+ @ws.disconnect { blk.call }
19
+ end
20
+
21
+ def onreceive(&blk)
22
+ @ws.stream { |data| blk.call(Base64.decode64(data)) }
23
+ end
24
+
25
+ def send_data(data)
26
+ # Um.. as soon as the em websocket object adds a better named
27
+ # method for this, start using it.
28
+ @ws.send(Base64.encode64(data))
29
+ end
30
+
31
+ def close
32
+ @ws.close_connection_after_writing
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,4 @@
1
+ # Copyright (c) 2009-2011 VMware, Inc.
2
+
3
+ require 'caldecott/server/http_tunnel'
4
+ require 'caldecott/server/websocket_tunnel'
@@ -0,0 +1,212 @@
1
+ # Copyright (c) 2009-2011 VMware, Inc.
2
+
3
+ require 'rubygems'
4
+ require 'logger'
5
+ require 'sinatra'
6
+ require 'sinatra/async'
7
+ require 'json'
8
+ require 'uuidtools'
9
+ require 'eventmachine'
10
+ require 'caldecott/tcp_connection.rb'
11
+ require 'caldecott/session_logger.rb'
12
+
13
+ module Caldecott
14
+ module Server
15
+ class Tunnel
16
+ attr_reader :tun_id, :log, :last_active_at
17
+ DEFAULT_MAX_DATA_TO_BUFFER = 1 * 1024 * 1024 # 1MB
18
+
19
+ def initialize(log, tunnels, host, port, max_data_to_buffer = DEFAULT_MAX_DATA_TO_BUFFER)
20
+ @log, @tunnels, @host, @port = log, tunnels, host, port
21
+ @tun_id = UUIDTools::UUID.random_create.to_s
22
+ @data = @data_next = ""
23
+ @seq_out = @seq_in = 0
24
+ @max_data_to_buffer = max_data_to_buffer
25
+ @last_active_at = Time.now
26
+ end
27
+
28
+ def open(resp)
29
+ EM::connect(@host, @port, TcpConnection) do |dst_conn|
30
+ @dst_conn = dst_conn
31
+
32
+ @dst_conn.onopen do
33
+ @log.debug "dst connected"
34
+ @tunnels[@tun_id] = self
35
+ resp.content_type :json
36
+ resp.status 201
37
+ resp.body safe_hash.to_json
38
+ end
39
+
40
+ @dst_conn.onreceive do |data|
41
+ @log.debug "t <- d #{data.length}"
42
+ @data_next << data
43
+ trigger_reader
44
+ @dst_conn.pause if @data_next.length > @max_data_to_buffer
45
+ end
46
+
47
+ @dst_conn.onclose do
48
+ @log.debug "target disconnected"
49
+ @dst_conn = nil
50
+ trigger_reader
51
+ @tunnels.delete(@tun_id) if @data_next.empty?
52
+ end
53
+ end
54
+ @tunnel_created_at = Time.now
55
+ end
56
+
57
+ def delete
58
+ @log.debug "target disconnected"
59
+ if @dst_conn
60
+ @dst_conn.close_connection_after_writing
61
+ else
62
+ @tunnels.delete(@tun_id)
63
+ end
64
+ end
65
+
66
+ def get(resp, seq)
67
+ @last_active_at = Time.now
68
+ resp.halt(400, "invalid sequence #{seq} for server seq #{@seq_out}") unless (seq == @seq_out or seq == @seq_out + 1)
69
+ if seq == @seq_out + 1
70
+ @data, @data_next = @data_next, ""
71
+ @seq_out = seq
72
+ end
73
+
74
+ if @data.empty?
75
+ resp.halt(410, "destination socket closed\n") if @dst_conn.nil?
76
+ @log.debug "get: waiting for data"
77
+ @reader = EM.Callback do
78
+ @data, @data_next = @data_next, ""
79
+ resp.ahalt(410, "destination socket closed\n") if @data.empty?
80
+ @log.debug "get: returning data (async)"
81
+ resp.body @data
82
+ end
83
+ else
84
+ @log.debug "get: returning data (immediate)"
85
+ resp.body @data
86
+ @dst_conn.resume
87
+ end
88
+ end
89
+
90
+ def put(resp, seq)
91
+ @last_active_at = Time.now
92
+ resp.halt(400, "invalid sequence #{seq} for server seq #{@seq_in}") unless (seq == @seq_in or seq == @seq_in + 1)
93
+ if seq == @seq_in
94
+ resp.status 201
95
+ else
96
+ @seq_in = seq
97
+ @log.debug "t -> d #{resp.request.body.length}"
98
+ @dst_conn.send_data(resp.request.body.read)
99
+ resp.status 202
100
+ end
101
+ end
102
+
103
+ def trigger_reader
104
+ return unless @reader
105
+ reader = @reader
106
+ @reader = nil
107
+ reader.call
108
+ end
109
+
110
+ def safe_hash
111
+ {
112
+ :path => "/tunnels/#{@tun_id}",
113
+ :path_in => "/tunnels/#{@tun_id}/in",
114
+ :path_out => "/tunnels/#{@tun_id}/out",
115
+ :dst_host => @host,
116
+ :dst_port => @port,
117
+ :dst_connected => @dst_conn.nil? == false,
118
+ :seq_out => @seq_out,
119
+ :seq_in => @seq_in
120
+ }
121
+ end
122
+
123
+ end
124
+
125
+ class HttpTunnel < Sinatra::Base
126
+ register Sinatra::Async
127
+
128
+ @@tunnels = {}
129
+
130
+ def self.tunnels
131
+ @@tunnels
132
+ end
133
+
134
+ # defaults are 1 hour of inactivity with sweeps every 5 minutes
135
+ def self.start_timer(inactive_timeout = 3600, sweep_interval = 300)
136
+ EventMachine::add_periodic_timer sweep_interval do
137
+ # This is needed because there seems to have a bug on the
138
+ # Connection#set_comm_inactivity_timeout (int overflow )
139
+ # Look at eventmachine/ext/em.cpp 2289
140
+ # It reaps the inactive connections
141
+ #
142
+ # We also can not seem to add our own timer per tunnel instance.
143
+ # When we do, the ruby interpreter freaks out and starts throwing
144
+ #
145
+ # errors like:
146
+ # undefined method `cancel' for 57:Fixnum
147
+ #
148
+ # for code like the following during shutdown:
149
+ # @inactivity_timer.cancel if @inactivity_timer
150
+ # @inactivity_timer.cancel
151
+ # @inactivity_timer = nil
152
+ @@tunnels.each do |id, t|
153
+ t.delete if (Time.now - t.last_active_at) > inactive_timeout
154
+ end
155
+ end
156
+ end
157
+
158
+ def tunnel_from_id(tun_id)
159
+ tun = @@tunnels[tun_id]
160
+ not_found("tunnel #{tun_id} does not exist\n") unless tun
161
+ tun.log.debug "#{request.request_method} #{request.url}"
162
+ tun
163
+ end
164
+
165
+ before do
166
+ @log = SessionLogger.new("server", STDOUT)
167
+ @log.debug "#{request.request_method} #{request.url}"
168
+ if env['HTTP_AUTH_TOKEN'] != settings.auth_token
169
+ @log.debug "AUTH FAILURE #{env.inspect}"
170
+ not_found
171
+ end
172
+ end
173
+
174
+ get '/' do
175
+ return "Caldecott Tunnel (HTTP Transport) #{VERSION}\n"
176
+ end
177
+
178
+ get '/tunnels' do
179
+ content_type :json
180
+ resp = @@tunnels.values.collect { |t| t.safe_hash }
181
+ resp.to_json
182
+ end
183
+
184
+ apost '/tunnels' do
185
+ req = JSON.parse(request.body.read, :symbolize_names => true)
186
+ Tunnel.new(@log, @@tunnels, req[:host], req[:port]).open(self)
187
+ end
188
+
189
+ get '/tunnels/:tun' do |tun_id|
190
+ tun = tunnel_from_id(tun_id)
191
+ tun.safe_hash.to_json
192
+ end
193
+
194
+ delete '/tunnels/:tun' do |tun_id|
195
+ tun = tunnel_from_id(tun_id)
196
+ tun.delete
197
+ end
198
+
199
+ aget '/tunnels/:tun_id/out/:seq' do |tun_id, seq|
200
+ tun = tunnel_from_id(tun_id)
201
+ seq = seq.to_i
202
+ tun.get(self, seq)
203
+ end
204
+
205
+ put '/tunnels/:tun_id/in/:seq' do |tun_id, seq|
206
+ tun = tunnel_from_id(tun_id)
207
+ seq = seq.to_i
208
+ tun.put(self, seq)
209
+ end
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,57 @@
1
+ # Copyright (c) 2009-2011 VMware, Inc.
2
+
3
+ require 'em-websocket'
4
+ require 'base64'
5
+
6
+ module Caldecott
7
+ module Server
8
+ class WebSocketTunnel
9
+
10
+ # quack like sinatra
11
+ def self.run!(opts)
12
+ WebSocketTunnel.new.start(opts[:port])
13
+ end
14
+
15
+ def start(port)
16
+ EM::WebSocket.start(:host => "0.0.0.0", :port => port) do |ws|
17
+ log = SessionLogger::new("server", STDOUT)
18
+ dst_conn = nil
19
+
20
+ ws.onopen do
21
+ log.debug "tunnel connected"
22
+ slash, tunnel, host, port = ws.request['Path'].split('/')
23
+
24
+ EM::connect(host, port, TcpConnection) do |d|
25
+ dst_conn = d
26
+
27
+ dst_conn.onopen do
28
+ log.debug "target connected"
29
+ end
30
+
31
+ dst_conn.onreceive do |data|
32
+ log.debug("t <- d #{data.length}")
33
+ ws.send(Base64.encode64(data))
34
+ end
35
+
36
+ dst_conn.onclose do
37
+ log.debug "target disconnected"
38
+ ws.close_connection
39
+ end
40
+ end
41
+ end
42
+
43
+ ws.onmessage do |msg|
44
+ decoded = Base64.decode64(msg)
45
+ log.debug("t -> d #{decoded.length}")
46
+ dst_conn.send_data(decoded)
47
+ end
48
+
49
+ ws.onclose do
50
+ log.debug "tunnel disconnected"
51
+ dst_conn.close_connection_after_writing if dst_conn
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,39 @@
1
+ # Copyright (c) 2009-2011 VMware, Inc.
2
+
3
+ require 'logger'
4
+
5
+ module Caldecott
6
+ class SessionLogger < Logger
7
+ attr_reader :component, :session
8
+ @@session = 0
9
+
10
+ def initialize(component, *args)
11
+ super(*args)
12
+ @component = component
13
+ @session = @@session += 1
14
+ end
15
+
16
+ def format_message(severity, timestamp, progname, msg)
17
+ "#{@component} [#{@session}] #{msg}\n"
18
+ end
19
+
20
+ def self.severity_from_string(str)
21
+ case str.upcase
22
+ when 'DEBUG'
23
+ Logger::DEBUG
24
+ when 'INFO'
25
+ Logger::INFO
26
+ when 'WARN'
27
+ Logger::WARN
28
+ when 'ERROR'
29
+ Logger::ERROR
30
+ when 'FATAL'
31
+ Logger::FATAL
32
+ when 'UNKNOWN'
33
+ Logger::UNKNOWN
34
+ else
35
+ Logger::ERROR
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,38 @@
1
+ # Copyright (c) 2009-2011 VMware, Inc.
2
+
3
+ require 'eventmachine'
4
+
5
+ module Caldecott
6
+ # wrapper to avoid callback and state passing spaghetti
7
+ class TcpConnection < EventMachine::Connection
8
+ @initialzied = false
9
+
10
+ # callbacks
11
+ def onopen(&blk)
12
+ @initialized ? blk.call : @onopen = blk
13
+ end
14
+
15
+ def onreceive(&blk)
16
+ @onreceive = blk
17
+ end
18
+
19
+ def onclose(&blk)
20
+ @onclose = blk
21
+ end
22
+
23
+ # handle EventMachine::Connection methods
24
+ def post_init
25
+ @initialized = true
26
+ @onopen.call if @onopen
27
+ end
28
+
29
+ def receive_data(data)
30
+ @onreceive.call(data) if @onreceive
31
+ end
32
+
33
+ def unbind
34
+ @onclose.call if @onclose
35
+ end
36
+
37
+ end
38
+ end
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2009-2011 VMware, Inc.
2
2
 
3
3
  module Caldecott
4
- VERSION = '0.0.1'
4
+ VERSION = '0.0.3'
5
5
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: caldecott
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.1
5
+ version: 0.0.3
6
6
  platform: ruby
7
7
  authors:
8
8
  - VMware
@@ -10,11 +10,131 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-10-20 00:00:00 -07:00
13
+ date: 2011-11-08 00:00:00 -08:00
14
14
  default_executable:
15
- dependencies: []
16
-
17
- description: TBD
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: em-http-request
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - "="
23
+ - !ruby/object:Gem::Version
24
+ version: 0.3.0
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: em-websocket
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - "="
34
+ - !ruby/object:Gem::Version
35
+ version: 0.3.1
36
+ type: :runtime
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: async_sinatra
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - "="
45
+ - !ruby/object:Gem::Version
46
+ version: 0.5.0
47
+ type: :runtime
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: addressable
51
+ prerelease: false
52
+ requirement: &id004 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - "="
56
+ - !ruby/object:Gem::Version
57
+ version: 2.2.6
58
+ type: :runtime
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: json
62
+ prerelease: false
63
+ requirement: &id005 !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - "="
67
+ - !ruby/object:Gem::Version
68
+ version: 1.6.1
69
+ type: :runtime
70
+ version_requirements: *id005
71
+ - !ruby/object:Gem::Dependency
72
+ name: uuidtools
73
+ prerelease: false
74
+ requirement: &id006 !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - "="
78
+ - !ruby/object:Gem::Version
79
+ version: 2.1.2
80
+ type: :runtime
81
+ version_requirements: *id006
82
+ - !ruby/object:Gem::Dependency
83
+ name: rake
84
+ prerelease: false
85
+ requirement: &id007 !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - "="
89
+ - !ruby/object:Gem::Version
90
+ version: 0.9.2
91
+ type: :development
92
+ version_requirements: *id007
93
+ - !ruby/object:Gem::Dependency
94
+ name: rcov
95
+ prerelease: false
96
+ requirement: &id008 !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - "="
100
+ - !ruby/object:Gem::Version
101
+ version: 0.9.10
102
+ type: :development
103
+ version_requirements: *id008
104
+ - !ruby/object:Gem::Dependency
105
+ name: rack-test
106
+ prerelease: false
107
+ requirement: &id009 !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - "="
111
+ - !ruby/object:Gem::Version
112
+ version: 0.6.1
113
+ type: :development
114
+ version_requirements: *id009
115
+ - !ruby/object:Gem::Dependency
116
+ name: rspec
117
+ prerelease: false
118
+ requirement: &id010 !ruby/object:Gem::Requirement
119
+ none: false
120
+ requirements:
121
+ - - "="
122
+ - !ruby/object:Gem::Version
123
+ version: 2.6.0
124
+ type: :development
125
+ version_requirements: *id010
126
+ - !ruby/object:Gem::Dependency
127
+ name: webmock
128
+ prerelease: false
129
+ requirement: &id011 !ruby/object:Gem::Requirement
130
+ none: false
131
+ requirements:
132
+ - - "="
133
+ - !ruby/object:Gem::Version
134
+ version: 1.7.6
135
+ type: :development
136
+ version_requirements: *id011
137
+ description: Caldecott HTTP/Websocket Tunneling Library
18
138
  email: support@vmware.com
19
139
  executables: []
20
140
 
@@ -26,7 +146,18 @@ extra_rdoc_files:
26
146
  files:
27
147
  - LICENSE
28
148
  - README.md
149
+ - lib/caldecott/client/client.rb
150
+ - lib/caldecott/client/http_tunnel.rb
151
+ - lib/caldecott/client/tunnel.rb
152
+ - lib/caldecott/client/websocket_tunnel.rb
153
+ - lib/caldecott/client.rb
154
+ - lib/caldecott/server/http_tunnel.rb
155
+ - lib/caldecott/server/websocket_tunnel.rb
156
+ - lib/caldecott/server.rb
157
+ - lib/caldecott/session_logger.rb
158
+ - lib/caldecott/tcp_connection.rb
29
159
  - lib/caldecott/version.rb
160
+ - lib/caldecott.rb
30
161
  has_rdoc: true
31
162
  homepage: http://vmware.com
32
163
  licenses: []
@@ -54,6 +185,6 @@ rubyforge_project:
54
185
  rubygems_version: 1.6.2
55
186
  signing_key:
56
187
  specification_version: 3
57
- summary: TBD
188
+ summary: Caldecott HTTP/Websocket Tunneling Library
58
189
  test_files: []
59
190