cf-uaac 1.3.1 → 1.3.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,10 @@
1
+ Cloud Foundry 2012.02.03 Beta
2
+ Copyright (c) [2009-2012] VMware, Inc. All Rights Reserved.
3
+
4
+ This product is licensed to you under the Apache License, Version 2.0 (the "License").
5
+ You may not use this product except in compliance with the License.
6
+
7
+ This product includes a number of subcomponents with
8
+ separate copyright notices and license terms. Your use of these
9
+ subcomponents is subject to the terms and conditions of the
10
+ subcomponent's license, as noted in the LICENSE file.
data/Rakefile CHANGED
@@ -10,7 +10,6 @@
10
10
  # subcomponent's license, as noted in the LICENSE file.
11
11
  #
12
12
 
13
- require "rdoc/task"
14
13
  require "rspec/core/rake_task"
15
14
  require "bundler/gem_tasks" # only available in bundler >= 1.0.15
16
15
  require "ci/reporter/rake/rspec"
@@ -27,11 +26,6 @@ RSpec::Core::RakeTask.new("test") do |t|
27
26
  t.pattern = "spec/**/*_spec.rb"
28
27
  end
29
28
 
30
- RDoc::Task.new do |rd|
31
- rd.rdoc_files.include("lib/**/*.rb")
32
- rd.rdoc_dir = "doc"
33
- end
34
-
35
29
  task :ci => [:pre_coverage, :rcov_reports, "ci:setup:rspec", :test]
36
30
  task :cov => [:pre_coverage, :test, :view_coverage]
37
31
  task :coverage => [:pre_coverage, :test]
data/bin/uaas CHANGED
@@ -2,6 +2,55 @@
2
2
 
3
3
  $:.unshift File.expand_path File.join __FILE__, '..', '..', 'lib'
4
4
  require 'stub/uaa'
5
+ require 'cli/base'
6
+ require 'cli/version'
7
+
8
+ module CF::UAA
9
+
10
+ Util.default_logger(:trace)
11
+
12
+ class ServerTopic < Topic
13
+ topic "", "run"
14
+ desc "version", "Display version" do say "version #{CLI_VERSION}" end
15
+ define_option :trace, "--[no-]trace", "-t", "display extra verbose debug information"
16
+ define_option :debug, "--[no-]debug", "-d", "display debug information"
17
+ define_option :help, "--[no-]help", "-h", "display helpful information"
18
+ define_option :version, "--[no-]version", "-v", "show version"
19
+
20
+ desc "help [topic|command...]", "Display summary or details of command or topic" do |*args|
21
+ args.empty? ? say_help : say_command_help(args)
22
+ end
23
+
24
+ define_option :port, "--port <number>", "-p", "port for server to listen on"
25
+ define_option :root, "--root <path>", "-r", "root path of UAA resources, e.g. /uaa"
26
+ desc "run", "Run the UAA server", :port, :root do
27
+ CF::UAA::StubUAA.new(port: (opts[:port] || "8080").to_i, root: opts[:root],
28
+ logger: Util.default_logger).run
29
+ end
30
+
31
+ end
32
+
33
+ class ServerCli < BaseCli
34
+ @overview = "UAA Stub Server Command Line\nProvides partial uaa server support suitable for testing uaac."
35
+ @topics = [ServerTopic]
36
+ @global_options = [:help, :version, :debug, :trace]
37
+ @input, @output = $stdin, $stdout
38
+
39
+ def self.handle_bad_command(args, msg)
40
+ @output.puts "\n#{msg}"
41
+ run args.unshift("help")
42
+ nil
43
+ end
44
+
45
+ def self.preprocess_options(args, opts)
46
+ return args.replace(["version"]) if opts[:version]
47
+ return args.unshift("help") if args.empty? || opts[:help] && args[0] != "version"
48
+ Util.default_logger(opts[:trace]? :trace: opts[:debug]? :debug: :warn, @output)
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+
55
+ exit CF::UAA::ServerCli.run ? 0 : 1
5
56
 
6
- CF::UAA::Util.default_logger(:trace)
7
- CF::UAA::StubUAA.new.run('localhost', 8080)
@@ -38,10 +38,10 @@ Gem::Specification.new do |s|
38
38
  s.add_development_dependency "simplecov"
39
39
  s.add_development_dependency "simplecov-rcov"
40
40
  s.add_development_dependency "ci_reporter"
41
- s.add_runtime_dependency "cf-uaa-lib", ">= 1.3.1"
41
+ s.add_runtime_dependency "cf-uaa-lib", ">= 1.3.3", "<= 1.3.3"
42
42
  s.add_runtime_dependency "highline"
43
43
  s.add_runtime_dependency "eventmachine"
44
44
  s.add_runtime_dependency "launchy"
45
45
  s.add_runtime_dependency "em-http-request", ">= 1.0.0.beta.3"
46
-
46
+ s.add_runtime_dependency "json_pure"
47
47
  end
@@ -13,6 +13,7 @@
13
13
 
14
14
  require 'highline'
15
15
  require 'optparse'
16
+ require 'json/pure'
16
17
 
17
18
  module CF; module UAA end end
18
19
 
@@ -56,10 +57,10 @@ class Topic
56
57
  @highline = HighLine.new(input, output)
57
58
  end
58
59
 
59
- def ask(prompt); @highline.ask("#{prompt}: ") end
60
- def ask_pwd(prompt); @highline.ask("#{prompt}: ") { |q| q.echo = '*' } end
61
- def say(msg); @output.puts(msg); msg end
62
- def gripe(msg); @output.puts(msg) end
60
+ def ask(prompt) @highline.ask("#{prompt}: ") end
61
+ def ask_pwd(prompt) @highline.ask("#{prompt}: ") { |q| q.echo = '*' } end
62
+ def say(msg) @output.puts(msg); msg end
63
+ def gripe(msg) @output.puts(msg) end
63
64
  def opts; @options end
64
65
 
65
66
  def terminal_columns
@@ -232,16 +233,15 @@ class BaseCli
232
233
  attr_accessor :overview, :topics, :global_options
233
234
  end
234
235
 
235
- def self.preprocess_options(args, opts); end # to be implemented in subclass
236
- def self.too_many_args(cmd); end # to be implemented in subclass
236
+ def self.preprocess_options(args, opts) end # to be implemented in subclass
237
+ def self.handle_bad_command(args, msg) end # to be implemented in subclass
237
238
 
238
239
  def self.run(args = ARGV)
239
240
  @input ||= $stdin
240
241
  @output ||= $stdout
241
- @option_defs = {}
242
242
  @output.string = "" if @output.respond_to?(:string)
243
243
  args = args.split if args.respond_to?(:split)
244
- @parser = OptionParser.new
244
+ @option_defs, @parser, orig = {}, OptionParser.new, args
245
245
  opts = @topics.each_with_object({}) do |tpc, o|
246
246
  tpc.option_defs.each do |k, optdef|
247
247
  @parser.on(*optdef) { |v| o[k] = v }
@@ -257,11 +257,14 @@ class BaseCli
257
257
  if v[:argc] == -1
258
258
  # variable args, leave args alone
259
259
  elsif args.length > v[:argc]
260
- too_many_args(v[:parts].dup)
261
- return nil
260
+ return handle_bad_command(orig, "Too many command line parameters given.")
262
261
  elsif args.length < v[:argc]
263
262
  (v[:argc] - args.length).times { args << nil }
264
263
  end
264
+ opts.keys.each do |o|
265
+ next if v[:options].include?(o) || @global_options.include?(o)
266
+ return handle_bad_command(orig, "Invalid option: #{o}")
267
+ end
265
268
  return tpc.new(self, opts, @input, @output).send(k, *args)
266
269
  end
267
270
  end
@@ -269,7 +272,7 @@ class BaseCli
269
272
  rescue Exception => e
270
273
  @output.puts "#{File.basename($0)} error", "#{e.class}: #{e.message}", (e.backtrace if opts[:trace])
271
274
  ensure
272
- puts @output.string if opts[:trace] && @print_on_trace
275
+ puts @output.string if opts[:trace] && @print_on_trace && @output.respond_to?(:string)
273
276
  end
274
277
 
275
278
  end
@@ -63,7 +63,7 @@ class ClientCli < CommonCli
63
63
  pp scim_request { |cr|
64
64
  opts[:client_id] = clientname(name)
65
65
  opts[:secret] = verified_pwd("New client secret", opts[:secret])
66
- defaults = opts[:clone] ? Util.hash_keys!(cr.get(opts[:clone]), :tosym) : {}
66
+ defaults = opts[:clone] ? Util.hash_keys!(cr.get(opts[:clone]), :sym) : {}
67
67
  defaults.delete(:client_id)
68
68
  cr.add(:client, client_info(defaults))
69
69
  }
@@ -73,7 +73,7 @@ class ClientCli < CommonCli
73
73
  :del_attrs, :interact do |name|
74
74
  pp scim_request { |cr|
75
75
  opts[:client_id] = clientname(name)
76
- orig = Util.hash_keys!(cr.get(:client, opts[:client_id]), :tosym)
76
+ orig = Util.hash_keys!(cr.get(:client, opts[:client_id]), :sym)
77
77
  info = client_info(orig)
78
78
  info.any? { |k, v| v != orig[k] } ? cr.put(:client, info) :
79
79
  gripe("Nothing to update. Use -i for interactive update.")
@@ -22,7 +22,7 @@ class Config
22
22
 
23
23
  def self.config; @config ? @config.dup : {} end
24
24
  def self.loaded?; !!@config end
25
- def self.yaml; YAML.dump(Util.hash_keys(@config, :tostr)) end
25
+ def self.yaml; YAML.dump(Util.hash_keys(@config, :str)) end
26
26
  def self.target?(tgt) tgt if @config[tgt = subhash_key(@config, tgt)] end
27
27
 
28
28
  # if a yaml string is provided, config is loaded from the string, otherwise
@@ -48,12 +48,12 @@ class Config
48
48
  else # file doesn't exist, make sure we can write it now
49
49
  File.open(@config_file, 'w') { |f| f.write("--- {}\n\n") }
50
50
  end
51
- Util.hash_keys!(@config, :tosym)
51
+ Util.hash_keys!(@config, :sym)
52
52
  @context = current_subhash(@config[@target][:contexts]) if @target = current_subhash(@config)
53
53
  end
54
54
 
55
55
  def self.save
56
- File.open(@config_file, 'w') { |f| YAML.dump(Util.hash_keys(@config, :tostr), f) } if @config_file
56
+ File.open(@config_file, 'w') { |f| YAML.dump(Util.hash_keys(@config, :str), f) } if @config_file
57
57
  true
58
58
  end
59
59
 
@@ -70,7 +70,7 @@ class Config
70
70
  raise ArgumentError, "target not set" unless @target
71
71
  return unless hash and !hash.empty?
72
72
  raise ArgumentError, "'contexts' is a reserved key" if hash.key?(:contexts)
73
- @config[@target].merge! Util.hash_keys(hash, :tosym)
73
+ @config[@target].merge! Util.hash_keys(hash, :sym)
74
74
  save
75
75
  end
76
76
 
@@ -111,7 +111,7 @@ class Config
111
111
  def self.add_opts(hash)
112
112
  raise ArgumentError, "target and context not set" unless @target && @context
113
113
  return unless hash and !hash.empty?
114
- @config[@target][:contexts][@context].merge! Util.hash_keys(hash, :tosym)
114
+ @config[@target][:contexts][@context].merge! Util.hash_keys(hash, :sym)
115
115
  save
116
116
  end
117
117
 
@@ -41,15 +41,17 @@ class GroupCli < CommonCli
41
41
  end
42
42
 
43
43
  desc "group delete [name]", "Delete group" do |name|
44
- pp scim_request { |ua|
44
+ pp scim_request { |ua|
45
45
  ua.delete(:delete, ua.id(:group, gname(name)))
46
- "success"
46
+ "success"
47
47
  }
48
48
  end
49
49
 
50
50
  def id_set(objs)
51
- objs.each_with_object(Set.new) {|o, s|
52
- s << (o.is_a?(String)? o: (o["id"] || o["value"]))
51
+ objs.each_with_object(Set.new) {|o, s|
52
+ id = o.is_a?(String)? o: (o["id"] || o["value"] || o["memberid"])
53
+ raise BadResponse, "no id found in response of current members" unless id
54
+ s << id
53
55
  }
54
56
  end
55
57
 
@@ -25,16 +25,17 @@ class Cli < BaseCli
25
25
  @topics = [MiscCli, InfoCli, TokenCli, UserCli, GroupCli, ClientCli]
26
26
  @global_options = [:help, :version, :debug, :trace, :config]
27
27
 
28
- def self.configure(config_file = "", input = $stdin, output = $stdout,
28
+ def self.configure(config_file = "", input = $stdin, output = $stdout,
29
29
  print_on_trace = false)
30
30
  @config_file, @input, @output = config_file, input, output
31
31
  @print_on_trace = print_on_trace
32
32
  self
33
33
  end
34
34
 
35
- def self.too_many_args(cmd)
36
- @output.puts "\nToo many command line parameters given."
37
- run cmd.unshift("help")
35
+ def self.handle_bad_command(args, msg)
36
+ @output.puts "\n#{msg}"
37
+ run args.unshift("help")
38
+ nil
38
39
  end
39
40
 
40
41
  def self.preprocess_options(args, opts)
@@ -27,7 +27,7 @@ class TokenCatcher < Stub::Base
27
27
  Config.target_value(:token_target))
28
28
  tkn = secret ? ti.authcode_grant(server.info.delete(:uri), data) :
29
29
  ti.implicit_grant(server.info.delete(:uri), data)
30
- server.info.update(Util.hash_keys!(tkn.info, :tosym))
30
+ server.info.update(Util.hash_keys!(tkn.info, :sym))
31
31
  reply.text "you are now logged in and can close this window"
32
32
  rescue TargetError => e
33
33
  reply.text "#{e.message}:\r\n#{Util.json_pretty(e.info)}\r\n#{e.backtrace}"
@@ -71,7 +71,8 @@ class TokenCli < CommonCli
71
71
 
72
72
  def issuer_request(client_id, secret = nil)
73
73
  update_target_info
74
- yield TokenIssuer.new(Config.target.to_s, client_id, secret, Config.target_value(:token_endpoint))
74
+ yield TokenIssuer.new(Config.target.to_s, client_id, secret,
75
+ :token_target => Config.target_value(:token_endpoint))
75
76
  rescue Exception => e
76
77
  complain e
77
78
  end
@@ -99,7 +100,7 @@ class TokenCli < CommonCli
99
100
  ti.implicit_grant_with_creds(creds, opts[:scope]).info
100
101
  }
101
102
  return gripe "attempt to get token failed\n" unless token && token["access_token"]
102
- tokinfo = TokenCoder.decode(token["access_token"], nil, nil, false)
103
+ tokinfo = TokenCoder.decode(token["access_token"], verify: false)
103
104
  Config.context = tokinfo["user_name"]
104
105
  Config.add_opts(user_id: tokinfo["user_id"])
105
106
  Config.add_opts token
@@ -140,8 +141,9 @@ class TokenCli < CommonCli
140
141
 
141
142
  def use_browser(client_id, secret = nil)
142
143
  catcher = Stub::Server.new(TokenCatcher,
143
- Util.default_logger(debug? ? :debug : trace? ? :trace : :info),
144
- client_id: client_id, client_secret: secret).run_on_thread("localhost", opts[:port])
144
+ logger: Util.default_logger(debug? ? :debug : trace? ? :trace : :info),
145
+ info: {client_id: client_id, client_secret: secret},
146
+ port: opts[:port]).run_on_thread
145
147
  uri = issuer_request(client_id, secret) { |ti|
146
148
  secret ? ti.authcode_uri("#{catcher.url}/authcode", opts[:scope]) :
147
149
  ti.implicit_uri("#{catcher.url}/callback", opts[:scope])
@@ -154,7 +156,7 @@ class TokenCli < CommonCli
154
156
  sleep 5
155
157
  print "."
156
158
  end
157
- Config.context = TokenCoder.decode(catcher.info[:access_token], nil, nil, false)[:user_name]
159
+ Config.context = TokenCoder.decode(catcher.info[:access_token], verify: false)[:user_name]
158
160
  Config.add_opts catcher.info
159
161
  say_success secret ? "authorization code" : "implicit"
160
162
  return unless opts[:vmc]
@@ -190,9 +192,9 @@ class TokenCli < CommonCli
190
192
  if opts[:client] && opts[:secret]
191
193
  pp Misc.decode_token(Config.target, opts[:client], opts[:secret], token, ttype)
192
194
  else
193
- seckey = opts[:key] || (Config.target_value(:signing_key) if Config.target_value(:signing_alg) !~ /rsa$/i)
194
- pubkey = opts[:key] || (Config.target_value(:signing_key) if Config.target_value(:signing_alg) =~ /rsa$/i)
195
- info = TokenCoder.decode(token, seckey, pubkey, seckey || pubkey)
195
+ seckey = opts[:key] || (Config.target_value(:signing_key) if Config.target_value(:signing_alg) !~ /rsa$/i)
196
+ pubkey = opts[:key] || (Config.target_value(:signing_key) if Config.target_value(:signing_alg) =~ /rsa$/i)
197
+ info = TokenCoder.decode(token, skey: seckey, pkey: pubkey, verify: !!(seckey || pubkey))
196
198
  say seckey || pubkey ? "\nValid token signature\n\n": "\nNote: no key given to validate token signature\n\n"
197
199
  pp info
198
200
  end
@@ -11,8 +11,9 @@
11
11
  # subcomponent's license, as noted in the LICENSE file.
12
12
  #++
13
13
 
14
+ # Cloud Foundry namespace
14
15
  module CF
15
16
  module UAA
16
- CLI_VERSION = "1.3.1"
17
+ CLI_VERSION = "1.3.3"
17
18
  end
18
19
  end
@@ -57,11 +57,11 @@ class StubScim
57
57
  :entitlements, :roles, :x509certificates, :name, :addresses,
58
58
  :authorizations, :groups].to_set,
59
59
  client: [*COMMON_ATTRS, :client_id, :client_secret, :authorities,
60
- :authorized_grant_types, :scope, :auto_approved_scope,
60
+ :authorized_grant_types, :scope, :auto_approved_scope,
61
61
  :access_token_validity, :refresh_token_validity, :redirect_uri].to_set,
62
62
  group: [*COMMON_ATTRS, :displayname, :members, :owners, :readers].to_set }
63
63
  VISIBLE_ATTRS = {user: Set.new(LEGAL_ATTRS[:user] - HIDDEN_ATTRS),
64
- client: Set.new(LEGAL_ATTRS[:client] - HIDDEN_ATTRS),
64
+ client: Set.new(LEGAL_ATTRS[:client] - HIDDEN_ATTRS),
65
65
  group: Set.new(LEGAL_ATTRS[:group] - HIDDEN_ATTRS)}
66
66
  ATTR_NAMES = LEGAL_ATTRS.each_with_object(Set.new) { |(k, v), s|
67
67
  v.each {|a| s << a.to_s }
@@ -76,15 +76,15 @@ class StubScim
76
76
  attr if ATTR_NAMES.include?(attr) && !HIDDEN_ATTRS.include?(attr = attr.to_sym)
77
77
  end
78
78
 
79
- def remove_attrs(stuff, attrs = HIDDEN_ATTRS)
79
+ def remove_attrs(stuff, attrs = HIDDEN_ATTRS)
80
80
  attrs.each { |a| stuff.delete(a.to_s) }
81
81
  stuff
82
82
  end
83
83
 
84
- def valid_id?(id, rtype)
85
- id && (t = @things_by_id[id]) && (rtype.nil? || t[:rtype] == rtype)
84
+ def valid_id?(id, rtype)
85
+ id && (t = @things_by_id[id]) && (rtype.nil? || t[:rtype] == rtype)
86
86
  end
87
-
87
+
88
88
  def ref_by_name(name, rtype) @things_by_name[rtype.to_s + name.downcase] end
89
89
 
90
90
  def ref_by_id(id, rtype = nil)
@@ -134,10 +134,10 @@ class StubScim
134
134
  end
135
135
 
136
136
  def input(stuff)
137
- thing = Util.hash_keys(stuff.dup, :tosym)
137
+ thing = Util.hash_keys(stuff.dup, :sym)
138
138
  REFERENCES.each {|a|
139
139
  next unless thing[a]
140
- thing[a] = thing[a].each_with_object(Set.new) { |r, s|
140
+ thing[a] = thing[a].each_with_object(Set.new) { |r, s|
141
141
  s << (r.is_a?(Hash)? r[:value] : r )
142
142
  }
143
143
  }
@@ -146,7 +146,7 @@ class StubScim
146
146
  thing[a] = thing[a].each_with_object({}) { |v, o|
147
147
  v = {value: v} unless v.is_a?(Hash)
148
148
  # enforce values are unique by type and value
149
- k = URI.encode_www_form(t: [v[:type], v: v[:value]]).downcase
149
+ k = Util.encode_form(t: [v[:type], v: v[:value]]).downcase
150
150
  o[k] = v
151
151
  }
152
152
  }
@@ -89,8 +89,7 @@ class Reply
89
89
  def initialize(status = 200) @status, @headers, @cookies, @body = status, {}, [], "" end
90
90
  def to_s
91
91
  reply = "HTTP/1.1 #{@status} OK\r\n"
92
- headers["server"] = "stub server"
93
- headers["date"] = DateTime.now.httpdate
92
+ headers["server"], headers["date"] = "stub server", DateTime.now.httpdate
94
93
  headers["content-length"] = body.bytesize
95
94
  headers.each { |k, v| reply << "#{k}: #{v}\r\n" }
96
95
  @cookies.each { |c| reply << "Set-Cookie: #{c}\r\n" }
@@ -168,8 +167,14 @@ class Base
168
167
  @server, @request, @reply, @match = server, Request.new, Reply.new, nil
169
168
  end
170
169
 
170
+ def default_route; reply_in_kind(404, error: "path not handled") end
171
+
171
172
  def process
172
173
  @reply = Reply.new
174
+ if server.root
175
+ return default_route unless request.path.start_with?(server.root)
176
+ request.path.slice!(0..server.root.length - 1)
177
+ end
173
178
  @match, handler = self.class.find_route(request)
174
179
  server.logger.debug "processing request to path #{request.path} for route #{@match ? @match.regexp : 'default'}"
175
180
  send handler
@@ -177,7 +182,7 @@ class Base
177
182
  server.logger.debug "replying to path #{request.path} with #{reply.body.length} bytes of #{reply.headers['content-type']}"
178
183
  #server.logger.debug "full reply is: #{reply.body.inspect}"
179
184
  rescue Exception => e
180
- server.logger.debug "exception from route handler: #{e.message}"
185
+ server.logger.debug "exception processing request: #{e.message}"
181
186
  server.trace { e.backtrace }
182
187
  reply_in_kind 500, e
183
188
  end
@@ -190,10 +195,6 @@ class Base
190
195
  end
191
196
  end
192
197
 
193
- def default_route
194
- reply_in_kind(404, error: "path not handled")
195
- end
196
-
197
198
  end
198
199
 
199
200
  #------------------------------------------------------------------------------
@@ -216,38 +217,62 @@ module Connection
216
217
  end
217
218
  end
218
219
 
219
- #--------------------------------------------------------------------------
220
+ #------------------------------------------------------------------------------
220
221
  class Server
221
- attr_reader :host, :port, :status, :logger
222
+
223
+ private
224
+
225
+ def done
226
+ fail unless @connections.empty?
227
+ EM.stop if @em_thread && EM.reactor_running?
228
+ @connections, @status, @sig, @em_thread = [], :stopped, nil, nil
229
+ sleep 0.1 unless EM.reactor_thread? # give EM a chance to stop
230
+ logger.debug EM.reactor_running?? "server done but EM still running": "server really done"
231
+ end
232
+
233
+ def initialize_connection(conn)
234
+ logger.debug "starting connection"
235
+ fail unless EM.reactor_thread?
236
+ @connections << conn
237
+ conn.req_handler, conn.comm_inactivity_timeout = @req_handler.new(self), 30
238
+ end
239
+
240
+ public
241
+
242
+ attr_reader :host, :port, :status, :logger, :root
222
243
  attr_accessor :info
223
244
  def url; "http://#{@host}:#{@port}" end
224
245
  def trace(msg = nil, &blk); logger.trace(msg, &blk) if logger.respond_to?(:trace) end
225
246
 
226
- def initialize(req_handler, logger = Logger.new($stdout), info = nil)
227
- @req_handler, @logger, @info = req_handler, logger, info
247
+ def initialize(req_handler, options)
248
+ @req_handler = req_handler
249
+ @logger = options[:logger] || Logger.new($stdout)
250
+ @info = options[:info]
251
+ @host = options[:host] || "localhost"
252
+ @init_port = options[:port] || 0
253
+ @root = options[:root]
228
254
  @connections, @status, @sig, @em_thread = [], :stopped, nil, nil
229
255
  end
230
256
 
231
- def start(hostname = "localhost", port = nil)
257
+ def start
232
258
  raise ArgumentError, "attempt to start a server that's already running" unless @status == :stopped
233
- @host = hostname
234
259
  logger.debug "starting #{self.class} server #{@host}"
235
260
  EM.schedule do
236
- @sig = EM.start_server(@host, port || 0, Connection) { |c| initialize_connection(c) }
261
+ @sig = EM.start_server(@host, @init_port, Connection) { |c| initialize_connection(c) }
237
262
  @port = Socket.unpack_sockaddr_in(EM.get_sockname(@sig))[0]
238
- logger.debug "#{self.class} server started at #{url}, signature #{@sig}"
263
+ logger.info "#{self.class} server started at #{url}"
239
264
  end
240
265
  @status = :running
241
266
  self
242
267
  end
243
268
 
244
- def run_on_thread(hostname = "localhost", port = 0)
269
+ def run_on_thread
245
270
  raise ArgumentError, "can't run on thread, EventMachine already running" if EM.reactor_running?
246
271
  logger.debug { "starting eventmachine on thread" }
247
272
  cthred = Thread.current
248
273
  @em_thread = Thread.new do
249
274
  begin
250
- EM.run { start(hostname, port); cthred.run }
275
+ EM.run { start; cthred.run }
251
276
  logger.debug "server thread done"
252
277
  rescue Exception => e
253
278
  logger.debug { "unhandled exception on stub server thread: #{e.message}" }
@@ -260,10 +285,10 @@ class Server
260
285
  self
261
286
  end
262
287
 
263
- def run(hostname = "localhost", port = 0)
288
+ def run
264
289
  raise ArgumentError, "can't run, EventMachine already running" if EM.reactor_running?
265
290
  @em_thread = Thread.current
266
- EM.run { start(hostname, port) }
291
+ EM.run { start }
267
292
  logger.debug "server and event machine done"
268
293
  end
269
294
 
@@ -286,25 +311,6 @@ class Server
286
311
  done if @status != :running && @connections.empty?
287
312
  end
288
313
 
289
- private
290
-
291
- def done
292
- fail unless @connections.empty?
293
- EM.stop if @em_thread && EM.reactor_running?
294
- @connections, @status, @sig, @em_thread = [], :stopped, nil, nil
295
- sleep 0.1 unless EM.reactor_thread? # give EM a chance to stop
296
- logger.debug EM.reactor_running? ?
297
- "server done but EM still running" : "server really done"
298
- end
299
-
300
- def initialize_connection(conn)
301
- logger.debug "starting connection"
302
- fail unless EM.reactor_thread?
303
- @connections << conn
304
- conn.req_handler = @req_handler.new(self)
305
- conn.comm_inactivity_timeout = 30
306
- end
307
-
308
314
  end
309
315
 
310
316
  end