1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 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 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
|
require "spec"
require "../spec_helper"
require "../../support/fibers"
private def wait_for(&)
timeout = {% if flag?(:interpreted) %}
# TODO: it's not clear why some interpreter specs
# take more than 5 seconds to bind to a server.
# See #12429.
25.seconds
{% else %}
5.seconds
{% end %}
now = Time.monotonic
until yield
Fiber.yield
if (Time.monotonic - now) > timeout
raise "server failed to start within #{timeout}"
end
end
end
# Helper method which runs *server*
# 1. Spawns `server.listen` in a new fiber.
# 2. Waits until `server.listening?`.
# 3. Yields to the given block.
# 4. Ensures the server is closed.
# 5. After returning from the block, it waits for the server to gracefully
# shut down before continuing execution in the current fiber.
# 6. If the listening fiber raises an exception, it is rescued and re-raised
# in the current fiber.
def run_server(server, &)
server_done = Channel(Exception?).new
f = spawn do
server.listen
rescue exc
server_done.send exc
else
server_done.send nil
end
begin
wait_for { server.listening? }
wait_until_blocked f
{% if flag?(:preview_mt) %}
# avoids fiber synchronization issues in specs, like closing the server
# before we properly listen, ...
sleep 1.millisecond
{% end %}
yield server_done
ensure
server.close unless server.closed?
if exc = server_done.receive
raise exc
end
end
end
# Helper method which runs a *handler*
# Similar to `run_server` but doesn't go through the network stack.
def run_handler(handler, &)
done = Channel(Exception?).new
IO::Stapled.pipe do |server_io, client_io|
processor = HTTP::Server::RequestProcessor.new(handler)
f = spawn do
processor.process(server_io, server_io)
rescue exc
done.send exc
else
done.send nil
end
client = HTTP::Client.new(client_io)
begin
wait_until_blocked f
yield client
ensure
processor.close
server_io.close
if exc = done.receive
raise exc
end
end
end
end
|