Class: HttpDecoy::Server
- Inherits:
-
Object
- Object
- HttpDecoy::Server
- Defined in:
- lib/http_decoy/server.rb
Overview
A real WEBrick HTTP server that runs in a background thread.
Uses WEBrick directly (no Rack::Handler) so it works with both Rack 2.x and Rack 3.x without the rackup gem.
Port 0 lets the OS pick a free port atomically — parallel test runners never collide.
Instance Attribute Summary collapse
-
#port ⇒ Object
readonly
Returns the value of attribute port.
-
#request_log ⇒ Object
readonly
Returns the value of attribute request_log.
-
#route_map ⇒ Object
readonly
Returns the value of attribute route_map.
Instance Method Summary collapse
- #base_url ⇒ Object
- #current_scenario ⇒ Object
-
#initialize(route_map) ⇒ Server
constructor
A new instance of Server.
-
#rack_app ⇒ Object
The Rack app is also exposed for WebMock's to_rack() interception.
- #start ⇒ Object
- #stop ⇒ Object
- #with_scenario(name) ⇒ Object
Constructor Details
#initialize(route_map) ⇒ Server
Returns a new instance of Server.
22 23 24 25 26 27 28 29 30 |
# File 'lib/http_decoy/server.rb', line 22 def initialize(route_map) @route_map = route_map @request_log = RequestLog.new @webrick = nil @thread = nil @port = nil @scenario = nil @scenario_mu = Mutex.new end |
Instance Attribute Details
#port ⇒ Object (readonly)
Returns the value of attribute port.
20 21 22 |
# File 'lib/http_decoy/server.rb', line 20 def port @port end |
#request_log ⇒ Object (readonly)
Returns the value of attribute request_log.
20 21 22 |
# File 'lib/http_decoy/server.rb', line 20 def request_log @request_log end |
#route_map ⇒ Object (readonly)
Returns the value of attribute route_map.
20 21 22 |
# File 'lib/http_decoy/server.rb', line 20 def route_map @route_map end |
Instance Method Details
#base_url ⇒ Object
79 |
# File 'lib/http_decoy/server.rb', line 79 def base_url = "http://127.0.0.1:#{@port}" |
#current_scenario ⇒ Object
93 94 95 |
# File 'lib/http_decoy/server.rb', line 93 def current_scenario @scenario_mu.synchronize { @scenario } end |
#rack_app ⇒ Object
The Rack app is also exposed for WebMock's to_rack() interception.
82 83 84 |
# File 'lib/http_decoy/server.rb', line 82 def rack_app @rack_app ||= build_rack_app end |
#start ⇒ Object
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/http_decoy/server.rb', line 32 def start rack_app = build_rack_app # WEBrick binds to the OS-assigned port during initialization. @webrick = WEBrick::HTTPServer.new( Port: 0, Logger: WEBrick::Log.new(File::NULL), AccessLog: [] ) @port = @webrick.config[:Port] @webrick.mount_proc("/") do |req, res| status, headers, body = rack_app.call(rack_env_from(req)) res.status = status.to_i headers.each { |k, v| res[k] = v } res.body = Array(body).join rescue Timeout::Error, Errno::ECONNRESET, Errno::ECONNREFUSED # raise_error(:timeout/:reset/:refused): over a real socket there's no # response that means "the connection died" — the only faithful # simulation is to actually kill the connection. (Over WebMock's # to_rack interception this same exception instead propagates directly # to the caller, which is the more common path and needs no help here.) drop_connection(req) rescue StandardError => e res.status = 500 res["Content-Type"] = "application/json" res.body = JSON.generate(error: "#{e.class}: #{e.}") end @thread = Thread.new { @webrick.start } @thread.abort_on_exception = true # Poll until WEBrick enters its accept loop. deadline = Time.now + 5 sleep(0.005) until @webrick.status == :Running || Time.now > deadline raise "http_decoy: server failed to start within 5 seconds" unless @webrick.status == :Running self end |
#stop ⇒ Object
72 73 74 75 76 77 |
# File 'lib/http_decoy/server.rb', line 72 def stop @webrick&.shutdown @thread&.join(3) @thread = nil @webrick = nil end |
#with_scenario(name) ⇒ Object
86 87 88 89 90 91 |
# File 'lib/http_decoy/server.rb', line 86 def with_scenario(name) @scenario_mu.synchronize { @scenario = name } yield ensure @scenario_mu.synchronize { @scenario = nil } end |