forward 0.2.1 → 0.3.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.
@@ -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