cf-uaac 4.30.0 → 5.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cd73b85e8be4bb6b6893ba09a5222db8eb46fead629fb6440dfd178e64f7f6ad
4
- data.tar.gz: 3282b758220eaa70a9242f8c997c3c88478d77522af5170a667c61d7335cbd77
3
+ metadata.gz: fd7760d3f031f0b442008a44934f109533b3c3603c3949c62bba59439a41eea7
4
+ data.tar.gz: b25c73922f6500e7b3e7a36c2a95390a6be055ea86a68b5ab3f73da2fdb45d34
5
5
  SHA512:
6
- metadata.gz: 2e34c12cae18539efed51c7c6ef16032edde303a1e0bc560c1c7fd941fa156ccffd04e56a531c0834ebb8c78f41f21477538d9321a59371a0ff779d286234486
7
- data.tar.gz: 816cfbd5731e629ce4de0bf0530625d9e5bf9f4c7e036fc0c39eb0dc30344096e16c2ba320d2b744081d1963f650f28e8101aa86f16b148e8ce6dd28af1956aa
6
+ metadata.gz: 58c337030166df2743c180ba2e2beeec9ff621e3c98875988a9d0c9a22f44db30a7926fcc9168697bf1faca0042ab64a066d205c8a304a07e0496d69c0b2df36
7
+ data.tar.gz: fd9603938d53dde1b7f48244bfdff6f5be857c40e2ffeba4e965a53b545a64e08dc81dc550b617181bf19d1728d376b9a9ae55f86993be5c940eeba3f11716e6
@@ -0,0 +1,105 @@
1
+ name: Release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ bump_type:
7
+ description: 'Type of version bump (major, minor, or patch)'
8
+ required: true
9
+ type: choice
10
+ default: 'patch'
11
+ options:
12
+ - major
13
+ - minor
14
+ - patch
15
+
16
+ run-name: "Ship ${{ inputs.bump_type }} Release"
17
+
18
+ concurrency:
19
+ group: serialize-releases-${{ github.repository }}
20
+ cancel-in-progress: false
21
+
22
+ permissions:
23
+ contents: write
24
+
25
+ jobs:
26
+ release:
27
+ name: Release gem and publish to RubyGems.org
28
+ runs-on: ubuntu-latest
29
+ if: github.ref == 'refs/heads/main'
30
+
31
+ steps:
32
+ - uses: actions/checkout@v7
33
+ with:
34
+ fetch-depth: 0
35
+
36
+ - name: Set up Ruby
37
+ uses: ruby/setup-ruby@v1
38
+ with:
39
+ bundler-cache: true
40
+ ruby-version: '3.3'
41
+
42
+ - name: Setup Git
43
+ run: |
44
+ git config --global user.email "identity-uaa+ci@pivotal.io"
45
+ git config --global user.name "UAA Identity Bot"
46
+
47
+ - name: Set up Node.js (for npx semver)
48
+ uses: actions/setup-node@v4
49
+ with:
50
+ node-version: 'lts/*'
51
+ - name: Bump version from last tag
52
+ id: bump_version
53
+ run: |
54
+ prev_tag=$(git tag -l 'v*' --sort=-v:refname | head -n1)
55
+ prev_tag=${prev_tag:-v0.0.0}
56
+ version=${prev_tag#v}
57
+ new_version=$(npx -y semver -i ${{ inputs.bump_type }} "${version}")
58
+
59
+ echo "${new_version}" > version.txt
60
+ git add version.txt
61
+ git commit -m "Bump version to ${new_version}"
62
+ git push origin HEAD:${{ github.ref_name }}
63
+
64
+ echo "version=${new_version}" >> "$GITHUB_OUTPUT"
65
+ echo "prev_tag=${prev_tag}" >> "$GITHUB_OUTPUT"
66
+ echo "🤔 *Previous tag: ${prev_tag}*" >> $GITHUB_STEP_SUMMARY
67
+ echo "### 👷 Building ${new_version}" >> $GITHUB_STEP_SUMMARY
68
+
69
+ - name: Configure RubyGems credentials
70
+ run: |
71
+ mkdir -p ~/.gem
72
+ printf -- "---\n:rubygems_api_key: %s\n" "${RUBYGEMS_AUTH_TOKEN}" > ~/.gem/credentials
73
+ chmod 600 ~/.gem/credentials
74
+ env:
75
+ RUBYGEMS_AUTH_TOKEN: ${{ secrets.RUBYGEMS_AUTH_TOKEN }}
76
+
77
+ - name: Release gem
78
+ run: bundle exec rake release
79
+
80
+ - name: Create GitHub release
81
+ id: create_release
82
+ env:
83
+ GH_TOKEN: ${{ github.token }}
84
+ run: |
85
+ version=${{ steps.bump_version.outputs.version }}
86
+ prev_tag=${{ steps.bump_version.outputs.prev_tag }}
87
+ tag="v${version}"
88
+
89
+ if git rev-parse -q --verify "refs/tags/${prev_tag}"; then
90
+ change_range="${prev_tag}...${tag}"
91
+ else
92
+ change_range="${tag}"
93
+ fi
94
+ git log --pretty='* %s' "${change_range}" > changelog.md
95
+
96
+ html_url=$(gh release create "${tag}" \
97
+ "pkg/cf-uaac-${version}.gem" \
98
+ --title "${tag}" \
99
+ --notes-file changelog.md)
100
+ echo "html_url=${html_url}" >> "$GITHUB_OUTPUT"
101
+
102
+ - name: Summary
103
+ run: |
104
+ echo "### 🎉 Created release ${{ steps.bump_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
105
+ echo "🌐 GitHub Release URL: ${{ steps.create_release.outputs.html_url }}" >> $GITHUB_STEP_SUMMARY
@@ -6,6 +6,9 @@ on:
6
6
  pull_request:
7
7
  branches: [ main ]
8
8
 
9
+ permissions:
10
+ contents: read
11
+
9
12
  jobs:
10
13
  test:
11
14
 
@@ -13,10 +16,10 @@ jobs:
13
16
  strategy:
14
17
  fail-fast: false
15
18
  matrix:
16
- ruby-version: ['2.5', '2.7', '3.2', '3.3', '3.4']
19
+ ruby-version: ['3.3', '3.4', '4.0']
17
20
 
18
21
  steps:
19
- - uses: actions/checkout@v6
22
+ - uses: actions/checkout@v7
20
23
  - name: Set up Ruby
21
24
  uses: ruby/setup-ruby@v1
22
25
  with:
data/Gemfile CHANGED
@@ -11,7 +11,7 @@
11
11
  # subcomponent's license, as noted in the LICENSE file.
12
12
  #++
13
13
 
14
- source 'http://rubygems.org'
14
+ source 'https://rubygems.org'
15
15
 
16
16
  # Specify your gem's dependencies in cf-uaac.gemspec
17
17
  gemspec
data/cf-uaac.gemspec CHANGED
@@ -25,13 +25,14 @@ Gem::Specification.new do |s|
25
25
  s.description = %q{Client command line tools for interacting with the CloudFoundry User Account and Authorization (UAA) server. The UAA is an OAuth2 Authorization Server so it can be used by webapps and command line apps to obtain access tokens to act on behalf of users. The tokens can then be used to access protected resources in a Resource Server. This library can be used by clients (as a convenient wrapper for mainstream oauth gems) or by resource servers.}
26
26
 
27
27
  s.license = 'Apache-2.0'
28
+ s.required_ruby_version = '>= 3.3'
28
29
  s.files = `git ls-files`.split("\n")
29
30
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
30
31
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
31
32
  s.require_paths = ['lib']
32
33
 
33
34
  # dependencies
34
- s.add_runtime_dependency 'cf-uaa-lib', '~> 4.0.9'
35
+ s.add_runtime_dependency 'cf-uaa-lib', '~> 4.0.10'
35
36
  s.add_development_dependency 'rake', '~> 13.0'
36
37
  s.add_development_dependency 'rspec', '~> 3.12'
37
38
  s.add_development_dependency 'simplecov', '~> 0.22.0'
@@ -39,9 +40,7 @@ Gem::Specification.new do |s|
39
40
  s.add_development_dependency 'ci_reporter', '~> 2.1.0'
40
41
  s.add_development_dependency 'ci_reporter_rspec', '~> 1.0'
41
42
  s.add_runtime_dependency 'highline', '>= 2', '< 4'
42
- s.add_runtime_dependency 'eventmachine', '~> 1.2'
43
43
  s.add_runtime_dependency 'launchy', '>= 2.5', '< 4.0'
44
- s.add_runtime_dependency 'em-http-request', '~> 1.1', '>= 1.1.2'
45
- s.add_runtime_dependency 'json', '~>2.7'
44
+ s.add_runtime_dependency 'json', '~> 2.19', '>= 2.19.3'
46
45
  s.add_runtime_dependency 'rack', '~> 3.2', '>= 3.2.5'
47
46
  end
@@ -11,7 +11,7 @@
11
11
  # subcomponent's license, as noted in the LICENSE file.
12
12
  #++
13
13
 
14
- require 'eventmachine'
14
+ require 'socket'
15
15
  require 'date'
16
16
  require 'logger'
17
17
  require 'pp'
@@ -33,7 +33,6 @@ class Request
33
33
  private
34
34
 
35
35
  def bslice(str, range)
36
- # byteslice is available in ruby 1.9.3
37
36
  str.respond_to?(:byteslice) ? str.byteslice(range) : str.slice(range)
38
37
  end
39
38
 
@@ -64,7 +63,7 @@ class Request
64
63
 
65
64
  public
66
65
 
67
- # adds data to the request, returns true if request is complete
66
+ # adds data to the request, returns truthy if request is complete
68
67
  def completed?(str)
69
68
  str, @prelude = @prelude + str, "" unless @prelude.empty?
70
69
  add_lines(str)
@@ -136,7 +135,6 @@ class Base
136
135
  attr_accessor :request, :reply, :match, :server
137
136
 
138
137
  def self.route(http_methods, matcher, filters = {}, &handler)
139
- fail unless !EM.reactor_running? || EM.reactor_thread?
140
138
  matcher = Regexp.new("^#{Regexp.escape(matcher.to_s)}$") unless matcher.is_a?(Regexp)
141
139
  filters = filters.each_with_object({}) { |(k, v), o|
142
140
  o[k.downcase] = v.is_a?(Regexp) ? v : Regexp.new("^#{Regexp.escape(v.to_s)}$")
@@ -156,7 +154,6 @@ class Base
156
154
  end
157
155
 
158
156
  def self.find_route(request)
159
- fail unless EM.reactor_thread?
160
157
  if @routes && (rary = @routes[request.method])
161
158
  rary.each { |r; m|
162
159
  next unless (m = r[0].match(request.path))
@@ -204,44 +201,60 @@ class Base
204
201
 
205
202
  end
206
203
 
207
- #------------------------------------------------------------------------------
208
- module Connection
209
- attr_accessor :req_handler
210
- def unbind; req_handler.server.delete_connection(self) end
211
-
212
- def receive_data(data)
213
- #req_handler.server.logger.debug "got #{data.bytesize} bytes: #{data.inspect}"
214
- return unless req_handler.request.completed? data
215
- req_handler.process
216
- send_data req_handler.reply.to_s
217
- if req_handler.reply.headers['connection'] =~ /^close$/i || req_handler.server.status != :running
218
- close_connection_after_writing
219
- end
220
- rescue Exception => e
221
- req_handler.server.logger.debug "exception from receive_data: #{e.message}"
222
- req_handler.server.trace { e.backtrace }
223
- close_connection
224
- end
225
- end
226
-
227
204
  #------------------------------------------------------------------------------
228
205
  class Server
229
206
 
230
207
  private
231
208
 
232
- def done
233
- fail unless @connections.empty?
234
- EM.stop if @em_thread && EM.reactor_running?
235
- @connections, @status, @sig, @em_thread = [], :stopped, nil, nil
236
- sleep 0.1 unless EM.reactor_thread? # give EM a chance to stop
237
- logger.debug EM.reactor_running?? "server done but EM still running": "server really done"
209
+ # Handle one TCP client socket: parse requests, dispatch, write replies.
210
+ # Supports keep-alive: the Request object resets itself after each
211
+ # completed? call, so the same req_handler is reused for pipelined requests.
212
+ def handle_client(socket)
213
+ req_handler = @req_handler.new(self)
214
+ loop do
215
+ # If leftover bytes from the previous request already complete the next
216
+ # one (pipelining / chunked reads), process without a blocking read.
217
+ unless req_handler.request.completed?("")
218
+ begin
219
+ data = socket.readpartial(4096)
220
+ rescue EOFError, Errno::ECONNRESET
221
+ break
222
+ end
223
+ next unless req_handler.request.completed?(data)
224
+ end
225
+ req_handler.process
226
+ socket.write(req_handler.reply.to_s)
227
+ break if req_handler.reply.headers['connection'] =~ /^close$/i || @status != :running
228
+ end
229
+ rescue Errno::ECONNRESET, Errno::EPIPE, IOError => e
230
+ logger.debug "connection error: #{e.message}"
231
+ ensure
232
+ socket.close rescue nil
233
+ @mutex.synchronize { @connections.delete(socket) }
234
+ logger.debug "connection closed"
238
235
  end
239
236
 
240
- def initialize_connection(conn)
241
- logger.debug "starting connection"
242
- fail unless EM.reactor_thread?
243
- @connections << conn
244
- conn.req_handler, conn.comm_inactivity_timeout = @req_handler.new(self), 30
237
+ # Accept connections in a loop until the server is stopped or the listening
238
+ # socket is closed.
239
+ def accept_loop
240
+ loop do
241
+ begin
242
+ socket = @tcp_server.accept_nonblock
243
+ @mutex.synchronize { @connections << socket }
244
+ logger.debug "starting connection"
245
+ Thread.new(socket) { |s| handle_client(s) }
246
+ rescue IO::WaitReadable, Errno::EINTR
247
+ IO.select([@tcp_server], nil, nil, 0.5) rescue nil
248
+ break if @status != :running
249
+ rescue Errno::EBADF, IOError
250
+ break
251
+ end
252
+ end
253
+ rescue => e
254
+ logger.debug "accept loop error: #{e.message}" unless e.is_a?(IOError) || e.is_a?(Errno::EBADF)
255
+ ensure
256
+ @status = :stopped
257
+ logger.debug "server really done"
245
258
  end
246
259
 
247
260
  public
@@ -258,64 +271,54 @@ class Server
258
271
  @host = options[:host] || "localhost"
259
272
  @init_port = options[:port] || 0
260
273
  @root = options[:root]
261
- @connections, @status, @sig, @em_thread = [], :stopped, nil, nil
274
+ @connections = []
275
+ @mutex = Mutex.new
276
+ @status = :stopped
277
+ @server_thread = nil
262
278
  end
263
279
 
264
280
  def start
265
281
  raise ArgumentError, "attempt to start a server that's already running" unless @status == :stopped
266
282
  logger.debug "starting #{self.class} server #{@host}"
267
- EM.schedule do
268
- @sig = EM.start_server(@host, @init_port, Connection) { |c| initialize_connection(c) }
269
- @port = Socket.unpack_sockaddr_in(EM.get_sockname(@sig))[0]
270
- logger.info "#{self.class} server started at #{url}"
271
- end
283
+ @tcp_server = TCPServer.new(@host, @init_port)
284
+ @port = @tcp_server.addr[1]
285
+ logger.info "#{self.class} server started at #{url}"
272
286
  @status = :running
273
287
  self
274
288
  end
275
289
 
290
+ # Start the server and run the accept loop on a background thread.
291
+ # Returns immediately; caller can use #url and #port right away.
276
292
  def run_on_thread
277
- raise ArgumentError, "can't run on thread, EventMachine already running" if EM.reactor_running?
278
- logger.debug { "starting eventmachine on thread" }
279
- cthred = Thread.current
280
- @em_thread = Thread.new do
281
- begin
282
- EM.run { start; cthred.run }
283
- logger.debug "server thread done"
284
- rescue Exception => e
285
- logger.debug { "unhandled exception on stub server thread: #{e.message}" }
286
- trace { e.backtrace }
287
- raise
288
- end
289
- end
290
- Thread.stop
293
+ raise ArgumentError, "can't run on thread, server already running" if @status == :running
294
+ logger.debug "starting server on thread"
295
+ start
296
+ @server_thread = Thread.new { accept_loop }
291
297
  logger.debug "running on thread"
292
298
  self
293
299
  end
294
300
 
301
+ # Start the server and run the accept loop on the calling thread (blocking).
295
302
  def run
296
- raise ArgumentError, "can't run, EventMachine already running" if EM.reactor_running?
297
- @em_thread = Thread.current
298
- EM.run { start }
299
- logger.debug "server and event machine done"
303
+ raise ArgumentError, "can't run, server already running" if @status == :running
304
+ @server_thread = Thread.current
305
+ start
306
+ accept_loop
307
+ logger.debug "server and event loop done"
300
308
  end
301
309
 
302
- # if on reactor thread, start shutting down but return if connections still
303
- # in process, and let them disconnect when complete -- server is not really
304
- # done until it's status is stopped.
305
- # if not on reactor thread, wait until everything's cleaned up and stopped
310
+ # Stop accepting new connections, close the listening socket, and wait for
311
+ # the accept loop thread to mark status :stopped.
306
312
  def stop
307
313
  logger.debug "stopping server"
308
314
  @status = :stopping
309
- EM.stop_server @sig
310
- done if @connections.empty?
311
- sleep 0.1 while @status != :stopped unless EM.reactor_thread?
315
+ @tcp_server.close rescue nil
316
+ sleep 0.05 while @status != :stopped
312
317
  end
313
318
 
314
319
  def delete_connection(conn)
315
320
  logger.debug "deleting connection"
316
- fail unless EM.reactor_thread?
317
- @connections.delete(conn)
318
- done if @status != :running && @connections.empty?
321
+ @mutex.synchronize { @connections.delete(conn) }
319
322
  end
320
323
 
321
324
  end
data/lib/uaa/stub/uaa.rb CHANGED
@@ -466,12 +466,12 @@ class StubUAAConn < Stub::Base
466
466
  route :get, %r{^/Groups/External/list(\?|$)(.*)} do
467
467
  return unless valid_token('scim.read')
468
468
 
469
- query_params = CGI::parse(match[2])
469
+ query_params = URI.decode_www_form(match[2]).to_h
470
470
 
471
- start_index_param = query_params['startIndex'].first
471
+ start_index_param = query_params['startIndex'] || ''
472
472
  start_index = start_index_param.empty? ? 1 : start_index_param.to_i
473
473
 
474
- count_param = query_params['count'].first
474
+ count_param = query_params['count'] || ''
475
475
  count = count_param.empty? ? 100 : count_param.to_i
476
476
 
477
477
  group_mappings = server.scim.get_group_mappings
data/spec/http_spec.rb CHANGED
@@ -14,7 +14,6 @@
14
14
  require 'spec_helper'
15
15
  require 'fiber'
16
16
  require 'net/http'
17
- require 'em-http'
18
17
  require 'uaa/http'
19
18
  require 'uaa/cli/version'
20
19
  require 'uaa/stub/server'
@@ -42,56 +41,45 @@ describe Http do
42
41
 
43
42
  after :all do @stub_http.stop if @stub_http end
44
43
 
45
- it "gets something from stub server on a fiber" do
46
- frequest(true) {
47
- f = Fiber.current
48
- http = EM::HttpRequest.new("#{@stub_http.url}/").get
49
- http.errback { f.resume "error" }
50
- http.callback {
51
- http.response_header.http_status.should == 200
52
- f.resume http.response
53
- }
54
- Fiber.yield
55
- }.should match /welcome to stub http/
56
- end
57
-
58
- it "uses persistent connections from stubserver" do
59
- frequest(true) {
60
- f = Fiber.current
61
- conn = EM::HttpRequest.new("#{@stub_http.url}/")
62
- req1 = conn.get keepalive: true
63
- req1.errback { f.resume "error1" }
64
- req1.callback {
65
- req2 = conn.get
66
- req2.errback { f.resume req2.error }
67
- req2.callback { f.resume req2.response }
68
- }
69
- Fiber.yield
70
- }.should match /welcome to stub http/
71
- end
72
-
73
44
  it "gets something from stub server on a thread" do
74
45
  @async = false
75
46
  resp = Net::HTTP.get(URI("#{@stub_http.url}/"))
76
47
  resp.should match /welcome to stub http/
77
48
  end
78
49
 
50
+ it "reuses connections to the same host (connection caching)" do
51
+ # Replaces the old EM keepalive test. The Http module caches one HTTPClient
52
+ # instance per host via @http_cache; verify multiple sequential requests
53
+ # all succeed through that cache.
54
+ client = HttpClient.new
55
+ 3.times do
56
+ status, body, _ = client.get(@stub_http.url, "/")
57
+ status.should == 200
58
+ body.should match /welcome to stub http/
59
+ end
60
+ end
61
+
62
+ it "works when called from a Ruby Fiber" do
63
+ # Replaces the old EM-fiber GET test. The HTTP client is synchronous, so
64
+ # calling it from a native Fiber must work without deadlocking.
65
+ result = nil
66
+ Fiber.new {
67
+ result = begin
68
+ status, body, _ = HttpClient.new.get(@stub_http.url, "/")
69
+ [status, body]
70
+ rescue => e
71
+ e
72
+ end
73
+ }.resume
74
+ result[0].should == 200
75
+ result[1].should match /welcome to stub http/
76
+ end
77
+
79
78
  shared_examples_for "http client" do
80
79
 
81
- # the following is intended to test that a failed dns lookup will fail the
82
- # same way on the buggy em-http-request 1.0.0.beta3 client as it does on
83
- # the rest-client. However, some networks (such as the one I am on now)
84
- # configure the dhcp client with a dns server that will resolve
85
- # every name as a valid address, e.g. bad.example.bad returns an address
86
- # to a service signup screen. I have tried stubbing the code in various
87
- # ways:
88
- # EventMachine.stub(:connect) { raise EventMachine::ConnectionError, "fake error for bad dns lookup" }
89
- # EventMachine.unstub(:connect)
90
- # Socket.stub(:gethostbyname) { raise SocketError, "getaddrinfo: Name or service not known" }
91
- # Socket.unstub(:gethostbyname)
92
- # This has had varied success but seems rather brittle. Currently I have opted
93
- # to just make the domain name invalid with tildes, but this may not test
94
- # the desired code paths
80
+ # the following is intended to test that a failed dns lookup will fail
81
+ # cleanly. Some networks resolve every name, so tildes are used to ensure
82
+ # an invalid hostname.
95
83
  it "fails cleanly for a failed dns lookup" do
96
84
  result = frequest(@on_fiber) { @client.get("http://bad~host~name/") }
97
85
  result.should be_an_instance_of BadTarget
@@ -125,39 +113,36 @@ describe Http do
125
113
  end
126
114
  end
127
115
 
128
- context "on a fiber" do
116
+ context "on a thread" do
129
117
  before :all do
130
- @on_fiber = true
118
+ @on_fiber = false
131
119
  @client = HttpClient.new
132
- @client.set_request_handler do |url, method, body, headers|
133
- f = Fiber.current
134
- connection = EventMachine::HttpRequest.new(url, connect_timeout: 2, inactivity_timeout: 2)
135
- client = connection.setup_request(method, head: headers, body: body)
136
-
137
- # This check is for proper error handling with em-http-request 1.0.0.beta.3
138
- if defined?(EventMachine::FailedConnection) && connection.is_a?(EventMachine::FailedConnection)
139
- raise BadTarget, "HTTP connection setup error: #{client.error}"
140
- end
141
-
142
- client.callback { f.resume [client.response_header.http_status, client.response, client.response_header] }
143
- client.errback { f.resume [:error, client.error] }
144
- result = Fiber.yield
145
- if result[0] == :error
146
- raise BadTarget, "connection failed" unless result[1] && result[1] != ""
147
- raise BadTarget, "connection refused" if result[1].to_s =~ /ECONNREFUSED/
148
- raise BadTarget, "unable to resolve address" if /unable.*resolve.*address/.match result[1]
149
- raise HTTPException, result[1]
150
- end
151
- [result[0], result[1], Util.hash_keys!(result[2], :dash)]
152
- end
153
120
  end
154
121
  it_should_behave_like "http client"
155
122
  end
156
123
 
157
- context "on a thread" do
124
+ # Replaces the old "on a fiber" context which ran the shared examples through
125
+ # an EventMachine async request handler via set_request_handler.
126
+ # Now exercises set_request_handler with a simple Net::HTTP backend,
127
+ # ensuring the callback interface itself works correctly.
128
+ context "with a custom request handler (Net::HTTP backend)" do
158
129
  before :all do
159
130
  @on_fiber = false
160
131
  @client = HttpClient.new
132
+ @client.set_request_handler do |url, method, body, headers|
133
+ uri = URI.parse(url)
134
+ http = Net::HTTP.new(uri.host, uri.port)
135
+ request = Net::HTTP::Get.new(uri.request_uri, headers)
136
+ begin
137
+ resp = http.request(request)
138
+ content_type = resp['content-type'] || ''
139
+ [resp.code.to_i, resp.body, {'content-type' => content_type}]
140
+ rescue SocketError, Errno::ECONNREFUSED, Errno::ECONNRESET => e
141
+ raise CF::UAA::BadTarget, e.message
142
+ rescue => e
143
+ raise CF::UAA::HTTPException, e.message
144
+ end
145
+ end
161
146
  end
162
147
  it_should_behave_like "http client"
163
148
  end
data/spec/spec_helper.rb CHANGED
@@ -23,7 +23,6 @@ if ENV['COVERAGE']
23
23
  end
24
24
 
25
25
  require 'rspec'
26
- require 'eventmachine'
27
26
  require 'uaa/stub/uaa'
28
27
 
29
28
  RSpec.configure do |config|
@@ -42,16 +41,11 @@ module SpecHelper
42
41
  e
43
42
  end
44
43
 
45
- # Runs given block on a thread or fiber and returns result.
46
- # If eventmachine is running on another thread, the fiber
47
- # must be on the same thread, hence EM.schedule and the
48
- # restriction that the given block cannot include rspec matchers.
44
+ # Runs the given block and returns the result (or a captured exception).
45
+ # The on_fiber parameter is retained for API compatibility; with EventMachine
46
+ # removed it simply executes on the current thread.
49
47
  def frequest(on_fiber, &blk)
50
- return capture_exception(&blk) unless on_fiber
51
- result, cthred = nil, Thread.current
52
- EM.schedule { Fiber.new { result = capture_exception(&blk); cthred.run }.resume }
53
- Thread.stop
54
- result
48
+ capture_exception(&blk)
55
49
  end
56
50
 
57
51
  def setup_target(opts = {})
@@ -100,4 +94,3 @@ module SpecHelper
100
94
  end
101
95
 
102
96
  end
103
-
@@ -13,7 +13,6 @@
13
13
 
14
14
  require 'spec_helper'
15
15
  require 'fiber'
16
- require 'em-http'
17
16
  require 'uaac_cli'
18
17
  require 'uaa/stub/server'
19
18
 
data/version.txt CHANGED
@@ -1 +1 @@
1
- 4.30.0
1
+ 5.0.0
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cf-uaac
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.30.0
4
+ version: 5.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dave Syer
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2026-02-27 00:00:00.000000000 Z
15
+ date: 2026-07-10 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: cf-uaa-lib
@@ -20,14 +20,14 @@ dependencies:
20
20
  requirements:
21
21
  - - "~>"
22
22
  - !ruby/object:Gem::Version
23
- version: 4.0.9
23
+ version: 4.0.10
24
24
  type: :runtime
25
25
  prerelease: false
26
26
  version_requirements: !ruby/object:Gem::Requirement
27
27
  requirements:
28
28
  - - "~>"
29
29
  - !ruby/object:Gem::Version
30
- version: 4.0.9
30
+ version: 4.0.10
31
31
  - !ruby/object:Gem::Dependency
32
32
  name: rake
33
33
  requirement: !ruby/object:Gem::Requirement
@@ -132,20 +132,6 @@ dependencies:
132
132
  - - "<"
133
133
  - !ruby/object:Gem::Version
134
134
  version: '4'
135
- - !ruby/object:Gem::Dependency
136
- name: eventmachine
137
- requirement: !ruby/object:Gem::Requirement
138
- requirements:
139
- - - "~>"
140
- - !ruby/object:Gem::Version
141
- version: '1.2'
142
- type: :runtime
143
- prerelease: false
144
- version_requirements: !ruby/object:Gem::Requirement
145
- requirements:
146
- - - "~>"
147
- - !ruby/object:Gem::Version
148
- version: '1.2'
149
135
  - !ruby/object:Gem::Dependency
150
136
  name: launchy
151
137
  requirement: !ruby/object:Gem::Requirement
@@ -167,39 +153,25 @@ dependencies:
167
153
  - !ruby/object:Gem::Version
168
154
  version: '4.0'
169
155
  - !ruby/object:Gem::Dependency
170
- name: em-http-request
156
+ name: json
171
157
  requirement: !ruby/object:Gem::Requirement
172
158
  requirements:
173
159
  - - "~>"
174
160
  - !ruby/object:Gem::Version
175
- version: '1.1'
161
+ version: '2.19'
176
162
  - - ">="
177
163
  - !ruby/object:Gem::Version
178
- version: 1.1.2
164
+ version: 2.19.3
179
165
  type: :runtime
180
166
  prerelease: false
181
167
  version_requirements: !ruby/object:Gem::Requirement
182
168
  requirements:
183
169
  - - "~>"
184
170
  - !ruby/object:Gem::Version
185
- version: '1.1'
171
+ version: '2.19'
186
172
  - - ">="
187
173
  - !ruby/object:Gem::Version
188
- version: 1.1.2
189
- - !ruby/object:Gem::Dependency
190
- name: json
191
- requirement: !ruby/object:Gem::Requirement
192
- requirements:
193
- - - "~>"
194
- - !ruby/object:Gem::Version
195
- version: '2.7'
196
- type: :runtime
197
- prerelease: false
198
- version_requirements: !ruby/object:Gem::Requirement
199
- requirements:
200
- - - "~>"
201
- - !ruby/object:Gem::Version
202
- version: '2.7'
174
+ version: 2.19.3
203
175
  - !ruby/object:Gem::Dependency
204
176
  name: rack
205
177
  requirement: !ruby/object:Gem::Requirement
@@ -241,6 +213,7 @@ extensions: []
241
213
  extra_rdoc_files: []
242
214
  files:
243
215
  - ".github/dependabot.yml"
216
+ - ".github/workflows/release.yml"
244
217
  - ".github/workflows/ruby.yml"
245
218
  - ".gitignore"
246
219
  - ".yardopts"
@@ -295,14 +268,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
295
268
  requirements:
296
269
  - - ">="
297
270
  - !ruby/object:Gem::Version
298
- version: '0'
271
+ version: '3.3'
299
272
  required_rubygems_version: !ruby/object:Gem::Requirement
300
273
  requirements:
301
274
  - - ">="
302
275
  - !ruby/object:Gem::Version
303
276
  version: '0'
304
277
  requirements: []
305
- rubygems_version: 3.4.19
278
+ rubygems_version: 3.5.22
306
279
  signing_key:
307
280
  specification_version: 4
308
281
  summary: Command line interface for CloudFoundry UAA