HTTP.AbortController (http_fetch v0.9.0)

Request cancellation mechanism similar to the browser's AbortController API.

This module provides a way to abort in-flight HTTP requests using an Agent-based controller. It's designed to work with the HTTP.fetch/2 function via the :signal option.

How It Works

  1. Create an AbortController before making the request
  2. Pass the controller to HTTP.fetch/2 via the :signal option
  3. Call abort/1 on the controller to cancel the request
  4. The awaiting Promise will receive an error result

Basic Usage

# Create a controller
controller = HTTP.AbortController.new()

# Start a long-running request with the controller
promise = HTTP.fetch("https://httpbin.org/delay/10",
  signal: controller,
  options: [timeout: 20_000]
)

# Abort the request (e.g., after 2 seconds)
:timer.sleep(2000)
HTTP.AbortController.abort(controller)

# The awaited promise will return an error
case HTTP.Promise.await(promise) do
  {:error, reason} ->
    IO.puts("Request was aborted: " <> inspect(reason))
  response ->
    IO.puts("Request completed before abort")
end

Advanced Usage

# Abort from another process
controller = HTTP.AbortController.new()

# Start request in background
Task.start(fn ->
  promise = HTTP.fetch("https://slow-api.example.com", signal: controller)
  result = HTTP.Promise.await(promise)
  IO.inspect(result)
end)

# Abort from main process after some condition
:timer.sleep(1000)
if some_condition?() do
  HTTP.AbortController.abort(controller)
end

Implementation Details

  • Uses Elixir's Agent for state management
  • Registers with a Registry for process tracking
  • Sends an abort message to the socket owner process
  • Thread-safe and can be called from any process
  • Idempotent - calling abort/1 multiple times is safe

State Management

The controller maintains the following state:

  • request_id - PID of the active socket owner process (set automatically)
  • signal_ref - Unique reference for registry lookup
  • aborted - Boolean flag indicating abort status

Summary

Functions

Aborts the associated HTTP request. Sends a stop signal if a request is active and not already aborted.

Checks if the controller has been aborted.

Returns a specification to start this module under a supervisor.

Creates a new AbortController instance. Returns the PID of the agent, which acts as the controller reference.

Sets the active request process for the given controller. This links the controller to an active request.

Starts a new AbortController agent. Returns {:ok, pid} of the agent.

Types

state()

@type state() :: %{
  request_id: pid() | nil,
  signal_ref: reference(),
  aborted: boolean()
}

Functions

abort(controller_pid)

@spec abort(pid()) :: :ok

Aborts the associated HTTP request. Sends a stop signal if a request is active and not already aborted.

aborted?(controller_pid)

@spec aborted?(pid()) :: boolean()

Checks if the controller has been aborted.

child_spec(arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

new()

@spec new() :: pid()

Creates a new AbortController instance. Returns the PID of the agent, which acts as the controller reference.

set_request_id(controller_pid, request_id)

@spec set_request_id(pid(), pid()) :: :ok

Sets the active request process for the given controller. This links the controller to an active request.

start_link(initial_state \\ %{request_id: nil, signal_ref: make_ref(), aborted: false})

@spec start_link(state()) :: {:ok, pid()}

Starts a new AbortController agent. Returns {:ok, pid} of the agent.