Current section

Files

Jump to
testcontainers lib wait_strategy http_wait_strategy.ex
Raw

lib/wait_strategy/http_wait_strategy.ex

# SPDX-License-Identifier: MIT
defmodule Testcontainers.WaitStrategy.HttpWaitStrategy do
@moduledoc """
Considers container as ready as soon as a command runs successfully inside the container.
"""
defstruct [:ip, :port, :path, :timeout]
@doc """
Creates a new CommandWaitStrategy to wait until the given command executes successfully inside the container.
"""
def new(ip, port, path \\ "/", timeout \\ 5000),
do: %__MODULE__{ip: ip, port: port, path: path, timeout: timeout}
end
defimpl Testcontainers.WaitStrategy, for: Testcontainers.WaitStrategy.HttpWaitStrategy do
alias Testcontainers.Container
alias Testcontainers.Docker
@impl true
def wait_until_container_is_ready(wait_strategy, id_or_name) do
with {:ok, %Container{} = container} <- Docker.Api.get_container(id_or_name) do
host_port = Container.mapped_port(container, wait_strategy.port)
case wait_for_http(wait_strategy.ip, host_port, wait_strategy.path, wait_strategy.timeout) do
{:ok, :http_is_ready} ->
:ok
_ ->
:timer.sleep(100)
wait_until_container_is_ready(wait_strategy, id_or_name)
end
end
end
defp wait_for_http(ip, port, path, timeout)
when is_binary(ip) and is_integer(port) and is_binary(path) and is_integer(timeout) do
wait_for_http(ip, port, path, timeout, :os.system_time(:millisecond))
end
defp wait_for_http(ip, port, path, timeout, start_time)
when is_binary(ip) and is_integer(port) and is_binary(path) and is_integer(timeout) and
is_integer(start_time) do
if timeout + start_time < :os.system_time(:millisecond) do
{:error, :timeout}
else
case http_request(ip, port, path) do
{:ok, _response} ->
{:ok, :http_is_ready}
{:error, _reason} ->
# Sleep for 500 ms, then retry
:timer.sleep(500)
wait_for_http(ip, port, path, timeout, start_time)
end
end
end
defp http_request(ip, port, path) do
url = "http://" <> ip <> ":" <> Integer.to_string(port) <> path
case :httpc.request(:get, {to_charlist(url), []}, [], []) do
{:ok, {{~c"HTTP/1.1", 200, _reason_phrase}, _headers, _body}} ->
{:ok, :http_ok}
{:ok, {{~c"HTTP/1.1", status_code, _reason_phrase}, _headers, _body}}
when status_code != 200 ->
{:error, {:unexpected_status_code, status_code}}
{:error, reason} ->
{:error, reason}
end
end
end