forward 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -56,8 +56,9 @@ module Forward
56
56
  end
57
57
 
58
58
  def self.debug!
59
- @debug = true
60
- logger.level = Logger::DEBUG
59
+ @logdev = STDOUT
60
+ @debug = true
61
+ log.level = Logger::DEBUG
61
62
  end
62
63
 
63
64
  def self.debug?
@@ -69,21 +70,22 @@ module Forward
69
70
  end
70
71
 
71
72
  def self.debug_remotely!
72
- logger(stringio_log)
73
- debug!
73
+ @logdev = stringio_log
74
+ @debug = true
74
75
  @debug_remotely = true
76
+ log.level = Logger::DEBUG
75
77
  end
76
78
 
77
79
  def self.debug_remotely?
78
- @debug_remotely
80
+ @debug_remotely ||= false
79
81
  end
80
82
 
81
- def self.log(level, message)
82
- logger.send(level, message) if debug?
83
+ def self.logdev
84
+ @logdev ||= '/dev/null'
83
85
  end
84
86
 
85
- def self.logger(logdev = STDOUT)
86
- @logger ||= Logger.new(logdev)
87
+ def self.log
88
+ @log ||= Logger.new(logdev)
87
89
  end
88
90
 
89
91
  # Returns a string representing a detailed client version.
@@ -96,7 +96,7 @@ module Forward
96
96
  end
97
97
 
98
98
  # def self.dispatch_error(error)
99
- # Forward.log(:debug, "Dispatching ResourceError: action: #{error.action} errors: #{error.errors.inspect}")
99
+ # Forward.log.debug("Dispatching ResourceError: action: #{error.action} errors: #{error.errors.inspect}")
100
100
  # method = :"#{error.action}_error"
101
101
  #
102
102
  # if respond_to? method
@@ -110,7 +110,7 @@ module Forward
110
110
 
111
111
  def log(level, message)
112
112
  unless self.class.to_s == 'Forward::Api::ClientLog'
113
- Forward.log(level, message)
113
+ Forward.log.send(level.to_sym, message)
114
114
  end
115
115
  end
116
116
 
@@ -10,8 +10,9 @@ module Forward
10
10
  :vhost => options[:host],
11
11
  :client => Forward.client_string,
12
12
  }
13
-
14
- [ :subdomain, :cname, :username, :password, :no_auth ].each do |param|
13
+
14
+ params[:subdomain] = options[:subdomain_prefix] if options.has_key?(:subdomain_prefix)
15
+ [ :cname, :username, :password, :no_auth ].each do |param|
15
16
  params[param] = options[param] if options.has_key?(param)
16
17
  end
17
18
 
@@ -27,15 +28,6 @@ module Forward
27
28
  resource.get[:tunnels]
28
29
  end
29
30
 
30
- def self.destroy(id)
31
- resource = Tunnel.new(:destroy)
32
- resource.uri = "/api/v2/tunnels/#{id}"
33
-
34
- resource.delete
35
- rescue ResourceError => e
36
- nil
37
- end
38
-
39
31
  def self.show(id)
40
32
  resource = Tunnel.new(:show)
41
33
  resource.uri = "/api/v2/tunnels/#{id}"
@@ -64,17 +56,17 @@ module Forward
64
56
  end
65
57
 
66
58
  def self.destroy_and_create(id, options)
67
- Forward.log(:debug, "Destroying tunnel: #{id}")
59
+ Forward.log.debug("Destroying tunnel: #{id}")
68
60
  destroy(id)
69
61
  puts "tunnel removed, now we're creating a new one"
70
62
  create(options)
71
63
  end
72
64
 
73
65
  def self.error_on_create(error, options)
74
- Forward.log(:debug, "An error occured creating tunnel:\n#{error.inspect}")
66
+ Forward.log.debug("An error occured creating tunnel:\n#{error.inspect}")
75
67
 
76
68
  if error.type == 'tunnel_limit_reached'
77
- Forward.log(:debug, 'Tunnel limit reached')
69
+ Forward.log.debug('Tunnel limit reached')
78
70
  ask_to_destroy(error.api_message, options)
79
71
  elsif error.type =~ /(?:account_suspended|trial_expired)/i
80
72
  Forward::Client.cleanup_and_exit!(error.api_message)
@@ -1,7 +1,10 @@
1
1
  module Forward
2
2
  class CLI
3
- CNAME_REGEX = /\A[a-z0-9]+(?:[\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}\z/i
4
- SUBDOMAIN_REGEX = /\A[a-z0-9]{1}[a-z0-9\-]+\z/i
3
+ BASIC_AUTH_REGEX = /\A[^\s:]+:[^\s:]+\z/i
4
+ CNAME_REGEX = /\A[a-z0-9]+(?:[\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}\z/i
5
+ SUBDOMAIN_PREFIX_REGEX = /\A[a-z0-9]{1}[a-z0-9\-]+\z/i
6
+ USERNAME_REGEX = PASSWORD_REGEX = /\A[^\s]+\z/i
7
+
5
8
  BANNER = <<-BANNER
6
9
  Usage: forward <port> [options]
7
10
  forward <host> [options]
@@ -20,7 +23,7 @@ module Forward
20
23
  > forward 3000
21
24
  Forward created at https://4ad3f-mycompany.fwd.wf
22
25
 
23
- Assigning a subdomain:
26
+ Assigning a subdomain prefix:
24
27
 
25
28
  > rails server &
26
29
  > forward 3000 myapp
@@ -37,22 +40,11 @@ module Forward
37
40
  BANNER
38
41
 
39
42
  # Parse non-published options and remove them from ARGV, then
40
- # parse published options and update the @options Hash with provided
43
+ # parse published options and update the options Hash with provided
41
44
  # options and removes switches from ARGV.
42
- def self.parse_options
43
- Forward.log(:debug, "Parsing options")
44
- @options = {
45
- :host => '127.0.0.1',
46
- :port => 80
47
- }
48
-
49
- if ARGV.include?('--debug')
50
- Forward.debug!
51
- ARGV.delete('--debug')
52
- elsif ARGV.include?('--debug-remotely')
53
- Forward.debug_remotely!
54
- ARGV.delete('--debug-remotely')
55
- end
45
+ def self.parse_cli_options
46
+ Forward.log.debug("Parsing options")
47
+ options = {}
56
48
 
57
49
  @opts = OptionParser.new do |opts|
58
50
  opts.banner = BANNER.gsub(/^ {6}/, '')
@@ -61,18 +53,18 @@ module Forward
61
53
  opts.separator 'Options:'
62
54
 
63
55
  opts.on('-a', '--auth [USER:PASS]', 'Protect this tunnel with HTTP Basic Auth.') do |credentials|
64
- username, password = parse_basic_auth(credentials)
65
- @options[:username] = username
66
- @options[:password] = password
56
+ exit_with_error("Basic Auth: bad format, expecting USER:PASS") if credentials !~ BASIC_AUTH_REGEX
57
+ username, password = credentials.split(':')
58
+ options[:username] = username
59
+ options[:password] = password
67
60
  end
68
61
 
69
62
  opts.on('-A', '--no-auth', 'Disable authentication on this tunnel (if a default is set in your preferences)') do |credentials|
70
- @options[:no_auth] = true
63
+ options[:no_auth] = true
71
64
  end
72
65
 
73
- opts.on('-c', '--cname [CNAME]', 'Allow access to this tunnel as NAME.') do |cname|
74
- validate_cname(cname)
75
- @options[:cname] = cname
66
+ opts.on('-c', '--cname [CNAME]', 'Allow access to this tunnel as CNAME (you will need to setup a CNAME entry on your DNS server).') do |cname|
67
+ options[:cname] = cname.downcase
76
68
  end
77
69
 
78
70
  opts.on( '-h', '--help', 'Display this help.' ) do
@@ -87,20 +79,54 @@ module Forward
87
79
  end
88
80
 
89
81
  @opts.parse!
82
+
83
+ options
84
+ end
85
+
86
+ # Returns a String file path for PWD/Forwardfile
87
+ def self.forwardfile_path
88
+ File.join(Dir.pwd, 'Forwardfile')
90
89
  end
91
90
 
92
- # Attempts to validate the basic auth credentials, if successful updates
93
- # the @options Hash with given credentials.
91
+ # Parse arguments from CLI and options from Forwardfile
94
92
  #
95
- # credentials - A String containing a username and password separated
96
- # by a colon.
93
+ # args - An Array of command line arguments
97
94
  #
98
- # Returns an Array containing the username and password
99
- def self.parse_basic_auth(credentials)
100
- validate_basic_auth(credentials)
101
- username, password = credentials.split(':')
95
+ # Returns a Hash of options.
96
+ def self.parse_args_and_options(args)
97
+ options = {
98
+ :host => '127.0.0.1',
99
+ :port => 80
100
+ }
101
+
102
+ Forward.log.debug("Default options: `#{options.inspect}'")
103
+
104
+ if File.exist? forwardfile_path
105
+ options.merge!(parse_forwardfile)
106
+ Forward.log.debug("Forwardfile options: `#{options.inspect}'")
107
+ end
108
+
109
+ options.merge!(parse_cli_options)
102
110
 
103
- [ username, password ]
111
+ forwarded, prefix = args[0..1]
112
+ options[:subdomain_prefix] = prefix unless prefix.nil?
113
+ options.merge!(parse_forwarded(forwarded))
114
+ Forward.log.debug("CLI options: `#{options.inspect}'")
115
+
116
+ options
117
+ end
118
+
119
+ # Parse a local Forwardfile (in the PWD) and return it as a Hash.
120
+ # Raise an error and exit if unable to parse or result isn't a Hash.
121
+ #
122
+ # Returns a Hash of the options found in the Forwardfile
123
+ def self.parse_forwardfile
124
+ options = YAML.load_file(forwardfile_path)
125
+ raise CLIError unless options.kind_of?(Hash)
126
+
127
+ options.symbolize_keys
128
+ rescue ArgumentError, SyntaxError, CLIError
129
+ exit_with_error("Unable to parse #{forwardfile_path}")
104
130
  end
105
131
 
106
132
  # Parses the arguments to determine if we're forwarding a port or host
@@ -110,12 +136,11 @@ module Forward
110
136
  #
111
137
  # Returns a Hash containing the forwarded host or port
112
138
  def self.parse_forwarded(arg)
113
- Forward.log(:debug, "Forwarded: `#{arg}'")
139
+ Forward.log.debug("Forwarded: `#{arg}'")
114
140
  forwarded = {}
115
141
 
116
142
  if arg =~ /\A\d{1,5}\z/
117
143
  port = arg.to_i
118
- validate_port(port)
119
144
 
120
145
  forwarded[:port] = port
121
146
  elsif arg =~ /\A[-a-z0-9\.\-]+\z/i
@@ -123,7 +148,6 @@ module Forward
123
148
  elsif arg =~ /\A[-a-z0-9\.\-]+:\d{1,5}\z/i
124
149
  host, port = arg.split(':')
125
150
  port = port.to_i
126
- validate_port(port)
127
151
 
128
152
  forwarded[:host] = host
129
153
  forwarded[:port] = port
@@ -135,89 +159,102 @@ module Forward
135
159
  # Checks to make sure the port being set is a number between 1 and 65535
136
160
  # and exits with an error message if it's not.
137
161
  #
138
- # port - A String containing the port number.
162
+ # port - port number Integer
139
163
  def self.validate_port(port)
140
- Forward.log(:debug, "Validating Port: `#{port}'")
164
+ Forward.log.debug("Validating Port: `#{port}'")
141
165
  unless port.between?(1, 65535)
142
166
  exit_with_error "Invalid Port: #{port} is an invalid port number"
143
167
  end
144
168
  end
145
169
 
146
- # Checks to make sure the basic auth credentials are in the correct format
147
- # and exits with an error message if they're not.
170
+ # Checks to make sure the username is a valid format
171
+ # and exits with an error message if not.
148
172
  #
149
- # credentials - A String with the username and password for basic auth.
150
- def self.validate_basic_auth(credentials)
151
- Forward.log(:debug, "Validating Basic Auth: `#{credentials}'")
152
- if credentials !~ /\A[^\s:]+:[^\s:]+\z/
153
- exit_with_error "Basic Auth: bad format, expecting USER:PASS"
154
- end
173
+ # username - username String
174
+ def self.validate_username(username)
175
+ Forward.log.debug("Validating Username: `#{username}'")
176
+ exit_with_error("`#{username}' is an invalid username format") unless username =~ USERNAME_REGEX
177
+ end
178
+
179
+ # Checks to make sure the password is a valid format
180
+ # and exits with an error message if not.
181
+ #
182
+ # password - password String
183
+ def self.validate_password(password)
184
+ Forward.log.debug("Validating Password: `#{password}'")
185
+ exit_with_error("`#{password}' is an invalid password format") unless password =~ PASSWORD_REGEX
155
186
  end
156
187
 
157
188
  # Checks to make sure the cname is in the correct format and exits with an
158
189
  # error message if it isn't.
159
190
  #
160
- # cname - A String containing the cname.
191
+ # cname - cname String
161
192
  def self.validate_cname(cname)
162
- Forward.log(:debug, "Validating CNAME: `#{cname}'")
193
+ Forward.log.debug("Validating CNAME: `#{cname}'")
163
194
  exit_with_error("`#{cname}' is an invalid domain format") unless cname =~ CNAME_REGEX
164
195
  end
165
196
 
166
- # Validates the subdomain and returns a Hash containing it.
197
+ # Checks to make sure the subdomain prefix is in the correct format
198
+ # and exits with an error message if it isn't.
167
199
  #
168
- # subdomain - A String containing the subdomain.
169
- #
170
- # Returns a Hash containing the subdomain
171
- def self.parse_subdomain(subdomain)
172
- validate_subdomain(subdomain)
173
- { :subdomain => subdomain }
200
+ # prefix - subdomain prefix String
201
+ def self.validate_subdomain_prefix(prefix)
202
+ Forward.log.debug("Validating Subdomain Prefix: `#{prefix}'")
203
+ exit_with_error("`#{prefix}' is an invalid subdomain prefix format") unless prefix =~ SUBDOMAIN_PREFIX_REGEX
174
204
  end
175
205
 
176
- # Checks to make sure the subdomain is in the correct format and exits with an
177
- # error message if it isn't.
206
+ # Validate all options in options Hash.
178
207
  #
179
- # cname - A String containing the subdomain.
180
- def self.validate_subdomain(subdomain)
181
- Forward.log(:debug, "Validating Subdomain: `#{subdomain}'")
182
- exit_with_error("`#{subdomain}' is an invalid subdomain format") unless subdomain =~ SUBDOMAIN_REGEX
183
- end
184
-
185
- def self.validate_options
186
- Forward.log(:debug, "Validating options: `#{@options.inspect}'")
208
+ # options - the options Hash
209
+ def self.validate_options(options)
210
+ Forward.log.debug("Validating options: `#{options.inspect}'")
211
+ options.each do |key, value|
212
+ next if value.nil?
213
+ validate_method = :"validate_#{key}"
214
+ send(validate_method, value) if respond_to?(validate_method)
215
+ end
187
216
  end
188
217
 
189
218
  # Asks for the user's email and password and puts them in a Hash.
190
219
  #
191
- # Returns a Hash with the email and password.
220
+ # Returns a Hash with the email and password
192
221
  def self.authenticate
193
222
  puts 'Enter your email and password'
194
223
  email = ask('email: ').chomp
195
224
  password = ask('password: ') { |q| q.echo = false }.chomp
196
- Forward.log(:debug, "Authenticating User: `#{email}:#{password.gsub(/./, 'x')}'")
225
+ Forward.log.debug("Authenticating User: `#{email}:#{password.gsub(/./, 'x')}'")
197
226
 
198
227
  { :email => email, :password => password }
199
228
  end
200
229
 
201
- # Parses various options and arguments, validates everything to ensure
202
- # we're safe to proceed, and finally passes @options to the Client.
230
+ # Parses various options and arguments, validates everything to ensure
231
+ # we're safe to proceed, and finally passes options to the Client.
203
232
  def self.run(args)
204
- Forward.log(:debug, "Starting forward v#{Forward::VERSION}")
205
- parse_options
206
- @options.merge!(parse_forwarded(args[0]))
207
- @options.merge!(parse_subdomain(args[1])) if args.length > 1
208
- validate_options
233
+ ::HighLine.use_color = false if Forward::Config.windows?
234
+ if ARGV.include?('--debug')
235
+ Forward.debug!
236
+ ARGV.delete('--debug')
237
+ elsif ARGV.include?('--rdebug')
238
+ Forward.debug_remotely!
239
+ ARGV.delete('--rdebug')
240
+ end
241
+
242
+ Forward.log.debug("Starting forward v#{Forward::VERSION}")
243
+
244
+ options = parse_args_and_options(args)
209
245
 
210
- print_usage_and_exit if args.empty?
246
+ validate_options(options)
247
+ print_usage_and_exit if args.empty? && !File.exist?(forwardfile_path)
211
248
 
212
- Client.start(@options)
249
+ Client.start(options)
213
250
  end
214
251
 
215
252
  # Colors an error message red and displays it.
216
253
  #
217
- # message - A String containing an error message.
254
+ # message - error message String
218
255
  def self.exit_with_error(message)
219
- Forward.log(:fatal, message)
220
- puts "\033[31m#{message}\033[0m"
256
+ Forward.log.fatal(message)
257
+ puts HighLine.color(message, :red)
221
258
  exit 1
222
259
  end
223
260
 
@@ -15,14 +15,14 @@ module Forward
15
15
 
16
16
  # Sets up a Tunnel instance and adds it to the Client.
17
17
  def setup_tunnel
18
- Forward.log(:debug, 'Setting up tunnel')
18
+ Forward.log.debug('Setting up tunnel')
19
19
  @tunnel = Forward::Tunnel.new(self.options)
20
20
  if @tunnel.id
21
21
  @tunnel.poll_status
22
22
  else
23
23
  Forward::Client.cleanup_and_exit!('Unable to create a tunnel. If this continues contact support@forwardhq.com')
24
24
  end
25
- Forward.log(:debug, "Tunnel setup: #{@tunnel.inspect}")
25
+ Forward.log.debug("Tunnel setup: #{@tunnel.inspect}")
26
26
 
27
27
  @tunnel
28
28
  end
@@ -50,10 +50,10 @@ module Forward
50
50
  end
51
51
 
52
52
  def self.forwarding_message(tunnel)
53
- remote = "\033[04mhttps://#{@tunnel.subdomain}.fwd.wf\033[0m"
53
+ remote = HighLine.color("https://#{@tunnel.subdomain}.fwd.wf", :underline)
54
54
 
55
55
  unless tunnel.cname.nil? || tunnel.cname.empty?
56
- remote << " and \033[04mhttp://#{@tunnel.cname}\033[0m"
56
+ remote << ' and '<< HighLine.color("http://#{@tunnel.cname}", :underline)
57
57
  end
58
58
 
59
59
  if !tunnel.vhost.nil? && tunnel.vhost !~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/
@@ -67,25 +67,24 @@ module Forward
67
67
  end
68
68
 
69
69
  def self.start(options = {})
70
- Forward.log(:debug, 'Starting client')
70
+ Forward.log.debug('Starting client')
71
71
  trap(:INT) { cleanup_and_exit!('closing tunnel and exiting...') }
72
72
 
73
73
  Forward.client = @client = Client.new(options)
74
74
  @tunnel = @client.setup_tunnel
75
75
  @session = Net::SSH.start(@tunnel.tunneler, Forward.ssh_user, @client.ssh_options)
76
76
 
77
- Forward.log(:debug, "Starting remote forward at #{@tunnel.subdomain}.fwd.wf")
78
- # puts "Forwarding port #{@tunnel.hostport} at \033[04mhttps://#{@tunnel.subdomain}.fwd.wf\033[0m\nCtrl-C to stop forwarding"
77
+ Forward.log.debug("Starting remote forward at #{@tunnel.subdomain}.fwd.wf")
79
78
  puts forwarding_message(@tunnel)
80
79
 
81
80
  @session.forward.remote(@tunnel.hostport, @tunnel.host, @tunnel.port)
82
81
  @session.loop { watch_session(@session) }
83
82
 
84
83
  rescue Net::SSH::AuthenticationFailed => e
85
- Forward.log(:fatal, "SSH Auth failed `#{e}'")
84
+ Forward.log.fatal("SSH Auth failed `#{e}'")
86
85
  cleanup_and_exit!("Authentication failed, try deleting `#{Forward::Config.config_path}' and giving it another go. If the problem continues, contact support@forwardhq.com")
87
86
  rescue => e
88
- Forward.log(:fatal, "#{e.message}\n#{e.backtrace.join("\n")}")
87
+ Forward.log.fatal("#{e.message}\n#{e.backtrace.join("\n")}")
89
88
  cleanup_and_exit!("You've been disconnected...")
90
89
  end
91
90
 
@@ -93,13 +92,8 @@ module Forward
93
92
  puts message.chomp
94
93
 
95
94
  @session.close if @session && !@session.closed?
96
- if @client && @client.tunnel && @client.tunnel.id
97
- Forward.log(:debug, "Cleaning up tunnel: `#{@client.tunnel.id}'")
98
- @client.tunnel.cleanup
99
- @client.tunnel = nil
100
- end
101
95
 
102
- Forward.log(:debug, 'Exiting')
96
+ Forward.log.debug('Exiting')
103
97
  send_debug_log if Forward.debug_remotely?
104
98
  ensure
105
99
  Thread.main.exit
@@ -25,7 +25,7 @@ module Forward
25
25
  #
26
26
  # Returns the updated Config object.
27
27
  def update(attributes)
28
- Forward.log(:debug, 'Updating Config')
28
+ Forward.log.debug('Updating Config')
29
29
  attributes.each do |key, value|
30
30
  self.send(:"#{key}=", value)
31
31
  end
@@ -43,7 +43,7 @@ module Forward
43
43
  # Validate that the required values are in the Config.
44
44
  # Raises a config error if values are missing.
45
45
  def validate
46
- Forward.log(:debug, 'Validating Config')
46
+ Forward.log.debug('Validating Config')
47
47
  attributes = [:api_token, :private_key]
48
48
  errors = []
49
49
 
@@ -65,7 +65,7 @@ module Forward
65
65
  #
66
66
  # Returns the Config object.
67
67
  def write
68
- Forward.log(:debug, 'Writing Config')
68
+ Forward.log.debug('Writing Config')
69
69
  key_folder = File.dirname(Config.key_path)
70
70
  config_data = to_hash.delete_if { |k,v| !CONFIG_FILE_VALUES.include?(k) }
71
71
 
@@ -141,7 +141,7 @@ module Forward
141
141
  #
142
142
  # Returns the new Config object.
143
143
  def self.create
144
- Forward.log(:debug, 'Creating Config')
144
+ Forward.log.debug('Creating Config')
145
145
  if @updating_config || ask('Already have an account with Forward? ').chomp =~ /\Ay/i
146
146
  config = Config.new
147
147
  email, password = CLI.authenticate.values_at(:email, :password)
@@ -151,7 +151,9 @@ module Forward
151
151
 
152
152
  config.write
153
153
  else
154
- Client.cleanup_and_exit!("You'll need a Forward account first. You can create one at \033[04mhttps://forwardhq.com\033[0m")
154
+ message = "You'll need a Forward account first. You can create one at "
155
+ message << HighLine.color('https://forwardhq.com', :underline)
156
+ Client.cleanup_and_exit!(message)
155
157
  end
156
158
  end
157
159
 
@@ -161,7 +163,7 @@ module Forward
161
163
  #
162
164
  # Returns the Config object.
163
165
  def self.load
164
- Forward.log(:debug, 'Loading Config')
166
+ Forward.log.debug('Loading Config')
165
167
  config = Config.new
166
168
 
167
169
  raise ConfigError, "Unable to find a forward config file at `#{config_path}'" unless Config.present?
@@ -1,6 +1,8 @@
1
1
  module Forward
2
2
  # An error occurred with the API
3
3
  class ApiError < StandardError; end
4
+ # An error occurred with the CLI
5
+ class CLIError < StandardError; end
4
6
  # An error occurred with the Client
5
7
  class ClientError < StandardError; end
6
8
  # An error occurred with the Config
@@ -45,11 +45,11 @@ module Forward
45
45
  Thread.new {
46
46
  loop do
47
47
  if @timeout && !@timeout.zero? && @inactive_for > @timeout
48
- Forward.log(:debug, "Session closing due to inactivity `#{@inactive_for}' seconds")
48
+ Forward.log.debug("Session closing due to inactivity `#{@inactive_for}' seconds")
49
49
  Client.cleanup_and_exit!("Tunnel has been inactive for #{@inactive_for} seconds, exiting...")
50
50
  elsif Forward::Api::Tunnel.show(@id).nil?
51
51
  Client.current.tunnel = nil
52
- Forward.log(:debug, "Tunnel destroyed, closing session")
52
+ Forward.log.debug("Tunnel destroyed, closing session")
53
53
  Client.cleanup_and_exit!
54
54
  else
55
55
  sleep CHECK_INTERVAL
@@ -59,11 +59,7 @@ module Forward
59
59
  end
60
60
  }
61
61
  end
62
-
63
- def cleanup
64
- Forward::Api::Tunnel.destroy(@id) if @id
65
- end
66
-
62
+
67
63
  def active?
68
64
  @active
69
65
  end
@@ -1,3 +1,3 @@
1
1
  module Forward
2
- VERSION = '0.2.1'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -87,24 +87,4 @@ describe Forward::Api::Tunnel do
87
87
  dev_null { Forward::Api::Tunnel.create(:port => 3000) }
88
88
  end
89
89
 
90
- it 'destroys a tunnel and returns the attributes' do
91
- fake_body = { :_id => '1', :subdomain => 'foo', :port => 56789 }
92
-
93
- stub_api_request(:delete, '/api/v2/tunnels/1', :body => fake_body.to_json)
94
-
95
- response = Forward::Api::Tunnel.destroy(1)
96
-
97
- fake_body.each do |key, value|
98
- response[key].must_equal fake_body[key]
99
- end
100
- end
101
-
102
- it 'gracefully handles the error if destroy has errors' do
103
- fake_body = { :type => 'api_error' }
104
-
105
- stub_api_request(:delete, '/api/v2/tunnels/1', :body => fake_body.to_json, :status => [ 422, 'Unprocessable Entity' ])
106
-
107
- Forward::Api::Tunnel.destroy(1).must_be_nil
108
- end
109
-
110
90
  end
@@ -22,23 +22,55 @@ describe Forward::CLI do
22
22
  forwarded[:port].must_equal 88
23
23
  end
24
24
 
25
- it 'parses valid basic auth and returns username and password' do
26
- username = 'foo'
27
- password = 'bar'
25
+ it 'exits if username is invalid' do
26
+ [ 'foo ', ' asdfasdf ', 'fooo bar' ].each do |username|
27
+ lambda {
28
+ dev_null { Forward::CLI.validate_username(username) }
29
+ }.must_raise SystemExit
30
+ end
31
+ end
28
32
 
29
- credentials = Forward::CLI.parse_basic_auth("#{username}:#{password}")
30
- credentials.first.must_equal username
31
- credentials.last.must_equal password
33
+ it 'validates a good username' do
34
+ [ 'foo', 'asdflkj3r&)(#@#)', 'DF#R::#SFSDF' ].each do |username|
35
+ Forward::CLI.validate_username(username).must_be_nil
36
+ end
32
37
  end
33
38
 
34
- it 'validates basic auth and exits if invalid' do
35
- [ 'afadsfsdf', 'adsf:', ':bar' ].each do |credentials|
39
+ it 'exits if password is invalid' do
40
+ [ 'foo ', ' asdfasdf ', 'fooo bar' ].each do |password|
36
41
  lambda {
37
- dev_null { Forward::CLI.validate_basic_auth(credentials) }
42
+ dev_null { Forward::CLI.validate_password(password) }
38
43
  }.must_raise SystemExit
39
44
  end
40
45
  end
41
46
 
47
+ it 'validates a good password' do
48
+ [ 'foo', 'asdflkj3r&)(#@#)', 'DF#R::#SFSDF' ].each do |password|
49
+ Forward::CLI.validate_password(password).must_be_nil
50
+ end
51
+ end
52
+
53
+ it 'parses a Forwardfile' do
54
+ yaml = YAML.dump(:auth => 'username:password')
55
+ File.open('Forwardfile', 'w') { |f| f.write(yaml) }
56
+
57
+ options = Forward::CLI.parse_forwardfile
58
+ options.has_key?(:auth).must_equal true
59
+ end
60
+
61
+ it 'exits if a Forwardfile does not parse to a Hash' do
62
+ yaml = YAML.dump('username:password')
63
+ File.open('Forwardfile', 'w') { |f| f.write(yaml) }
64
+
65
+ lambda {
66
+ dev_null { Forward::CLI.parse_forwardfile }
67
+ }.must_raise SystemExit
68
+ end
69
+
70
+ it 'overloads Forwardfile options via commandline' do
71
+ flunk
72
+ end
73
+
42
74
  it 'doesnt exit on valid ports' do
43
75
  Forward::CLI.validate_port(69).must_be_nil
44
76
  Forward::CLI.validate_port(3000).must_be_nil
@@ -67,16 +99,16 @@ describe Forward::CLI do
67
99
  end
68
100
  end
69
101
 
70
- it 'doesnt exit on valid subdomains' do
102
+ it 'doesnt exit on valid subdomains prefix' do
71
103
  [ 'foo', 'whatever-foo', 'asdf40' ].each do |subdomain|
72
- Forward::CLI.validate_subdomain(subdomain).must_be_nil
104
+ Forward::CLI.validate_subdomain_prefix(subdomain).must_be_nil
73
105
  end
74
106
  end
75
107
 
76
- it 'validates subdomain and exits if invalid' do
108
+ it 'validates subdomain prefix and exits if invalid' do
77
109
  [ '-asdf', 'adsf#$)' ].each do |subdomain|
78
110
  lambda {
79
- dev_null { Forward::CLI.validate_subdomain(subdomain) }
111
+ dev_null { Forward::CLI.validate_subdomain_prefix(subdomain) }
80
112
  }.must_raise SystemExit
81
113
  end
82
114
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forward
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-03 00:00:00.000000000 Z
12
+ date: 2013-01-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: json