Packages
testcontainers
1.1.3
2.3.1
2.3.0
2.2.0
2.1.0
2.1.0-rc1
2.0.0
2.0.0-rc3
2.0.0-rc2
2.0.0-rc
1.14.1
1.14.0
1.13.5
1.13.4
1.13.3
1.13.2
1.13.1
1.13.0
1.12.0
1.11.8
1.11.7
1.11.6
1.11.5
1.11.4
1.11.3
1.11.2
1.11.1
1.11.0
1.10.5
1.10.4
1.10.3
1.10.2
1.10.1
1.10.0
1.9.0
1.8.4
1.8.3
1.8.2
1.8.1
1.8.0
1.7.0
1.6.0
1.5.1
1.5.0
1.4.2
1.4.1
1.4.0
1.3.1
1.3.0
1.2.11
1.2.10
1.2.9
1.2.8
1.2.7
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.1.3
1.1.2
1.1.1
1.1.0
1.0.1
1.0.0
1.0.0-beta.1
1.0.0-beta
0.9.3
0.9.2
0.9.1
0.9.0
Testcontainers is an Elixir library that supports ExUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container.
Current section
Files
Jump to
Current section
Files
lib/wait_strategy/http_wait_strategy.ex
# SPDX-License-Identifier: MIT
defmodule Testcontainers.WaitStrategy.HttpWaitStrategy do
@moduledoc """
Awaits a successful HTTP response from the container.
"""
@retry_delay 200
defstruct [:ip, :port, :path, :status_code, :timeout, retry_delay: @retry_delay]
@doc """
Creates a new HttpWaitStrategy to wait until a successful HTTP response is received from the container.
"""
def new(
ip,
port,
path \\ "/",
status_code \\ 200,
timeout \\ 5000,
retry_delay \\ @retry_delay
),
do: %__MODULE__{
ip: ip,
port: port,
path: path,
status_code: status_code,
timeout: timeout,
retry_delay: retry_delay
}
end
defimpl Testcontainers.WaitStrategy, for: Testcontainers.WaitStrategy.HttpWaitStrategy do
alias Testcontainers.Container
alias Testcontainers.Connection
alias Testcontainers.Utils
def wait_until_container_is_ready(wait_strategy, container_id) do
with {:ok, %Container{} = container} <- Connection.get_container(container_id) do
host_port = Container.mapped_port(container, wait_strategy.port)
case wait_for_http(
container_id,
wait_strategy,
host_port,
current_time_millis()
) do
{:ok, :http_is_ready} ->
:ok
{:error, reason} ->
{:error, reason, wait_strategy}
end
end
end
defp wait_for_http(container_id, wait_strategy, host_port, start_time)
when is_integer(host_port) and is_integer(start_time) do
if wait_strategy.timeout + start_time < current_time_millis() do
{:error, strategy_timed_out(wait_strategy.timeout, start_time)}
else
case http_request(
wait_strategy.ip,
host_port,
wait_strategy.path,
wait_strategy.status_code
) do
{:ok, _response} ->
{:ok, :http_is_ready}
{:error, _reason} ->
delay = max(0, wait_strategy.retry_delay)
Utils.log(
"Http endpoint #{"http://#{wait_strategy.ip}:#{host_port}#{wait_strategy.path}"} in container #{container_id} didnt respond with #{wait_strategy.status_code}, retrying in #{delay}ms."
)
:timer.sleep(delay)
wait_for_http(container_id, wait_strategy, host_port, start_time)
end
end
end
defp http_request(ip, port, path, expected_status_code) when is_integer(expected_status_code) do
url = "http://" <> ip <> ":" <> Integer.to_string(port) <> path
case :httpc.request(:get, {to_charlist(url), []}, [], []) do
{:ok, {{~c"HTTP/1.1", ^expected_status_code, _reason_phrase}, _headers, _body}} ->
{:ok, :http_ok}
{:ok, {{~c"HTTP/1.1", status_code, _reason_phrase}, _headers, _body}}
when status_code != expected_status_code ->
{:error, {:unexpected_status_code, status_code}}
{:error, reason} ->
{:error, reason}
end
end
defp current_time_millis, do: System.monotonic_time(:millisecond)
defp strategy_timed_out(timeout, started_at) when is_number(timeout) and is_number(started_at),
do: {:http_wait_strategy, :timeout, timeout, elapsed_time: current_time_millis() - started_at}
end