### Elixir: Connect and Request with Mint.HTTP Source: https://context7.com/context7/hexdocs_pm_mint_v1_7_1/llms.txt Demonstrates connecting to an HTTPS server, sending a GET request with custom headers, and streaming the response using Mint.HTTP. This module handles automatic protocol negotiation between HTTP/1.1 and HTTP/2. It manages connections as data structures, allowing direct lifecycle control. ```elixir {:ok, conn} = Mint.HTTP.connect(:https, "api.example.com", 443) {:ok, conn, request_ref} = Mint.HTTP.request( conn, "GET", "/users/123", [{"accept", "application/json"}], "" ) receive do message -> {:ok, conn, responses} = Mint.HTTP.stream(conn, message) Enum.each(responses, fn {:status, ^request_ref, status} -> IO.puts("Status: #{status}") {:headers, ^request_ref, headers} -> IO.inspect(headers, label: "Headers") {:data, ^request_ref, data} -> IO.puts("Body: #{data}") {:done, ^request_ref} -> IO.puts("Request complete") end) {:ok, conn} = Mint.HTTP.close(conn) end ``` -------------------------------- ### Mint.HTTP - Connection Management Source: https://context7.com/context7/hexdocs_pm_mint_v1_7_1/llms.txt Provides protocol-agnostic HTTP connection functions, automatically selecting between HTTP/1.1 and HTTP/2. ```APIDOC ## Mint.HTTP - Connection Management ### Description Handles core HTTP connection logic, including automatic protocol negotiation (HTTP/1.1 or HTTP/2) based on server capabilities. ### Method - `connect/3`: Connects to a specified host and port. - `request/5`: Sends an HTTP request over an established connection. - `stream/2`: Streams responses associated with a connection. - `close/1`: Closes an established connection. ### Endpoint N/A (This is a module for programmatic interaction, not a REST endpoint) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```elixir # Connect to an HTTPS server with automatic protocol negotiation {:ok, conn} = Mint.HTTP.connect(:https, "api.example.com", 443) # Send a GET request {:ok, conn, request_ref} = Mint.HTTP.request( conn, "GET", "/users/123", [{"accept", "application/json"}], "" ) # Receive and process responses receive do message -> {:ok, conn, responses} = Mint.HTTP.stream(conn, message) Enum.each(responses, fn {:status, ^request_ref, status} -> IO.puts("Status: #{status}") {:headers, ^request_ref, headers} -> IO.inspect(headers, label: "Headers") {:data, ^request_ref, data} -> IO.puts("Body: #{data}") {:done, ^request_ref} -> IO.puts("Request complete") end) # Close the connection {:ok, conn} = Mint.HTTP.close(conn) end ``` ### Response #### Success Response (200) Responses are received via Elixir's `receive` mechanism as messages. Common message types include `{:status, request_ref, status_code}`, `{:headers, request_ref, headers_list}`, `{:data, request_ref, data_chunk}`, and `{:done, request_ref}`. #### Response Example ```elixir # Example message received via receive block: {:ok, %Mint.Conn{...}, [{:status, :my_request_ref, 200}, {:headers, :my_request_ref, [{"content-type", "application/json"}]}, {:data, :my_request_ref, "{\"id\": 123}"}, {:done, :my_request_ref}]} ``` ``` -------------------------------- ### Mint.HTTP2 - HTTP/2 Client Source: https://context7.com/context7/hexdocs_pm_mint_v1_7_1/llms.txt A process-less client for HTTP/2 connections, supporting multiplexing, server push, and flow control. ```APIDOC ## Mint.HTTP2 - HTTP/2 Client ### Description A process-less client implementation for the HTTP/2 protocol, enabling multiplexing of requests over a single connection, handling server push, and managing flow control. ### Method - `connect/4`: Establishes an HTTP/2 connection with optional transport and client settings. - `request/5`: Sends an HTTP/2 request. - `stream/2`: Streams responses for HTTP/2 connections. - `ping/2`: Sends a ping frame for keep-alive. ### Endpoint N/A (This is a module for programmatic interaction, not a REST endpoint) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```elixir # Establish HTTP/2 connection with custom settings {:ok, conn} = Mint.HTTP2.connect(:https, "http2.example.com", 443, transport_opts: [verify: :verify_peer], client_settings: [max_concurrent_streams: 100] ) # Send concurrent requests over single connection {:ok, conn, ref1} = Mint.HTTP2.request(conn, "GET", "/api/users", [], "") {:ok, conn, ref2} = Mint.HTTP2.request(conn, "GET", "/api/posts", [], "") {:ok, conn, ref3} = Mint.HTTP2.request(conn, "GET", "/api/comments", [], "") # Handle server push receive do message -> {:ok, conn, responses} = Mint.HTTP2.stream(conn, message) Enum.each(responses, fn {:push_promise, request_ref, promised_ref, headers} -> IO.puts("Server push: #{inspect(headers)}") {:headers, ref, headers} -> IO.inspect(headers) {:data, ref, data} -> IO.write(data) {:done, ref} -> IO.puts("\nRequest #{inspect(ref)} complete") end) end # Ping for keepalive {:ok, conn, ping_ref} = Mint.HTTP2.ping(conn) ``` ### Response #### Success Response (200) Responses for HTTP/2 are received through `receive` blocks and can include `{:push_promise, ...}` messages in addition to standard status, headers, and data. Responses to concurrent requests may arrive out of order. #### Response Example ```elixir # Example of receiving responses and server push promises: receive do {:ok, conn, responses} -> Enum.each(responses, fn {:push_promise, :req1, :promised_req_id, [(':path', '/resource/1')]} -> IO.puts("Push promise received.") {:headers, :req1, [(':status', '200'), ('content-type', 'application/json')]} {:data, :req1, "{\"data\": \"content\"}"} {:done, :req1} {:headers, :req2, [(':status', '404')]} {:done, :req2} end) end ``` ``` -------------------------------- ### Elixir: HTTP/2 Connection and Multiplexing with Mint.HTTP2 Source: https://context7.com/context7/hexdocs_pm_mint_v1_7_1/llms.txt Illustrates establishing an HTTP/2 connection with custom settings and sending concurrent requests over a single connection using Mint.HTTP2. It also includes handling server push promises and performing keepalive pings. This module supports multiplexing, server push, and flow control. ```elixir {:ok, conn} = Mint.HTTP2.connect(:https, "http2.example.com", 443, transport_opts: [verify: :verify_peer], client_settings: [max_concurrent_streams: 100] ) {:ok, conn, ref1} = Mint.HTTP2.request(conn, "GET", "/api/users", [], "") {:ok, conn, ref2} = Mint.HTTP2.request(conn, "GET", "/api/posts", [], "") {:ok, conn, ref3} = Mint.HTTP2.request(conn, "GET", "/api/comments", [], "") receive do message -> {:ok, conn, responses} = Mint.HTTP2.stream(conn, message) Enum.each(responses, fn {:push_promise, request_ref, promised_ref, headers} -> IO.puts("Server push: #{inspect(headers)}") {:headers, ref, headers} -> IO.inspect(headers) {:data, ref, data} -> IO.write(data) {:done, ref} -> IO.puts("\nRequest #{inspect(ref)} complete") end) end {:ok, conn, ping_ref} = Mint.HTTP2.ping(conn) ``` -------------------------------- ### Mint.HTTP1 - HTTP/1.1 Client Source: https://context7.com/context7/hexdocs_pm_mint_v1_7_1/llms.txt Provides explicit control over HTTP/1.1 connections, supporting features like request pipelining and request body streaming. ```APIDOC ## Mint.HTTP1 - HTTP/1.1 Client ### Description A process-less client for HTTP/1.1 connections, offering explicit control for scenarios requiring HTTP/1.1 specific features like request pipelining. ### Method - `connect/4`: Establishes an explicit HTTP/1.1 connection. - `request/5`: Sends an HTTP/1.1 request. - `stream_request_body/3`: Streams the request body in chunks. - `open?/1`: Checks if the connection is currently open. ### Endpoint N/A (This is a module for programmatic interaction, not a REST endpoint) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```elixir # Establish an HTTP/1.1 connection explicitly {:ok, conn} = Mint.HTTP1.connect(:https, "example.com", 443, []) # Send multiple pipelined requests {:ok, conn, ref1} = Mint.HTTP1.request(conn, "GET", "/api/status", [], "") {:ok, conn, ref2} = Mint.HTTP1.request(conn, "GET", "/api/metrics", [], "") # Stream request body for large payloads {:ok, conn, ref3} = Mint.HTTP1.request( conn, "POST", "/upload", [{"content-type", "application/octet-stream"}], :stream ) {:ok, conn} = Mint.HTTP1.stream_request_body(conn, ref3, "chunk1") {:ok, conn} = Mint.HTTP1.stream_request_body(conn, ref3, "chunk2") {:ok, conn} = Mint.HTTP1.stream_request_body(conn, ref3, :eof) # Check connection state Mint.HTTP1.open?(conn) # => true ``` ### Response #### Success Response (200) Similar to `Mint.HTTP`, responses are handled via `receive` blocks. For pipelined requests, responses are typically received in the order they were sent, though this can depend on server behavior. #### Response Example ```elixir # Example of receiving responses for pipelined requests: receive do {:ok, conn, responses} -> Enum.each(responses, fn {:status, ^ref1, 200} {:headers, ^ref1, [...]} {:done, ^ref1} {:status, ^ref2, 200} {:headers, ^ref2, [...]} {:done, ^ref2} {:status, ^ref3, 201} {:done, ^ref3} end) end ``` ``` -------------------------------- ### Elixir: Explicit HTTP/1.1 Connection with Mint.HTTP1 Source: https://context7.com/context7/hexdocs_pm_mint_v1_7_1/llms.txt Shows how to establish an explicit HTTP/1.1 connection using Mint.HTTP1. It covers sending multiple pipelined requests and streaming request bodies for large payloads. This module is for scenarios requiring specific HTTP/1.1 features and offers explicit protocol control. ```elixir {:ok, conn} = Mint.HTTP1.connect(:https, "example.com", 443, []) {:ok, conn, ref1} = Mint.HTTP1.request(conn, "GET", "/api/status", [], "") {:ok, conn, ref2} = Mint.HTTP1.request(conn, "GET", "/api/metrics", [], "") {:ok, conn, ref3} = Mint.HTTP1.request( conn, "POST", "/upload", [{"content-type", "application/octet-stream"}], :stream ) {:ok, conn} = Mint.HTTP1.stream_request_body(conn, ref3, "chunk1") {:ok, conn} = Mint.HTTP1.stream_request_body(conn, ref3, "chunk2") {:ok, conn} = Mint.HTTP1.stream_request_body(conn, ref3, :eof) Mint.HTTP1.open?(conn) # => true ``` -------------------------------- ### Elixir Connection Pooling with MintGenServer Source: https://context7.com/context7/hexdocs_pm_mint_v1_7_1/llms.txt Implements a connection pool for the Mint HTTP client using Elixir's GenServer. This pattern is suitable for high-concurrency scenarios by managing a fixed number of reusable connections to a host. It handles checking out and checking in connections, ensuring efficient resource utilization. ```elixir defmodule MintPool do use GenServer def start_link(opts) do GenServer.start_link(__MODULE__, opts, name: __MODULE__) end def init(opts) do scheme = Keyword.get(opts, :scheme, :https) host = Keyword.fetch!(opts, :host) port = Keyword.get(opts, :port, 443) pool_size = Keyword.get(opts, :pool_size, 10) # Create initial pool of connections connections = for _ <- 1..pool_size do case Mint.HTTP.connect(scheme, host, port) do {:ok, conn} -> {:available, conn} {:error, _} -> :disconnected end end {:ok, %{ scheme: scheme, host: host, port: port, connections: connections, waiting: :queue.new() }} end def handle_call(:checkout, from, state) do case find_available(state.connections) do {index, conn} -> connections = List.replace_at(state.connections, index, {:in_use, conn, from}) {:reply, {:ok, conn}, %{state | connections: connections}} nil -> waiting = :queue.in(from, state.waiting) {:noreply, %{state | waiting: waiting}} end end def handle_cast({:checkin, conn}, state) do case :queue.out(state.waiting) do {{:value, from}, waiting} -> GenServer.reply(from, {:ok, conn}) connections = add_in_use(state.connections, conn, from) {:noreply, %{state | connections: connections, waiting: waiting}} {:empty, _} -> connections = add_available(state.connections, conn) {:noreply, %{state | connections: connections}} end end defp find_available(connections) do Enum.find_index(connections, fn {:available, _conn} -> true _ -> false end) |> case do nil -> nil index -> {index, elem(Enum.at(connections, index), 1)} end end defp add_available(connections, conn) do index = Enum.find_index(connections, fn {:in_use, c, _} when c == conn -> true _ -> false end) List.replace_at(connections, index, {:available, conn}) end defp add_in_use(connections, conn, from) do # Find first available slot index = Enum.find_index(connections, fn :disconnected -> true {:available, _} -> true _ -> false end) List.replace_at(connections, index, {:in_use, conn, from}) end end # Usage {:ok, _} = MintPool.start_link(host: "api.example.com", pool_size: 20) {:ok, conn} = GenServer.call(MintPool, :checkout) {:ok, conn, ref} = Mint.HTTP.request(conn, "GET", "/api/data", [], "") # ... handle response ... GenServer.cast(MintPool, {:checkin, conn}) ``` -------------------------------- ### Handle Mint HTTP Request Errors Source: https://context7.com/context7/hexdocs_pm_mint_v1_7_1/llms.txt Demonstrates how to handle various error scenarios that can occur during an HTTP request made with Mint. This includes connection closed, protocol errors, and reaching the maximum request limit. ```elixir case Mint.HTTP.request(conn, "GET", "/api/data", [], "") do {:ok, conn, request_ref} -> {:ok, conn, request_ref} {:error, conn, %Mint.HTTPError{reason: reason}} -> case reason do :closed -> # Connection closed unexpectedly IO.puts("Connection closed, reconnecting...") Mint.HTTP.connect(:https, "api.example.com", 443) {:protocol, _details} -> # HTTP protocol violation IO.puts("Protocol error: #{inspect(reason)}") {:error, :protocol_error} {:max_requests_reached, _} -> # HTTP/1.1 connection limit reached IO.puts("Max requests reached, opening new connection") {:error, :need_new_connection} other -> IO.puts("HTTP error: #{inspect(other)}") {:error, reason} end end ``` -------------------------------- ### Elixir Streaming Large HTTP Responses with Mint Source: https://context7.com/context7/hexdocs_pm_mint_v1_7_1/llms.txt Demonstrates how to download large files efficiently using Mint's streaming capabilities. This approach avoids loading the entire response into memory by processing data in chunks, making it suitable for very large downloads. It handles connection, request, and file writing iteratively. ```elixir defmodule StreamingDownload do def download_file(url, output_path) do uri = URI.parse(url) {:ok, conn} = Mint.HTTP.connect( String.to_atom(uri.scheme), uri.host, uri.port || 443 ) {:ok, conn, request_ref} = Mint.HTTP.request( conn, "GET", uri.path || "/", [], "" ) File.open!(output_path, [:write, :binary], fn file -> stream_to_file(conn, request_ref, file) end) Mint.HTTP.close(conn) :ok end defp stream_to_file(conn, request_ref, file, total_bytes \\ 0) do receive do message -> case Mint.HTTP.stream(conn, message) do {:ok, conn, responses} -> {done?, bytes} = process_streaming_responses( responses, request_ref, file, total_bytes ) if done? do IO.puts("\nDownloaded #{bytes} bytes") :ok else stream_to_file(conn, request_ref, file, bytes) end {:error, _conn, error, _responses} -> {:error, error} end after 30_000 -> {:error, :timeout} end end defp process_streaming_responses(responses, ref, file, total) do Enum.reduce(responses, {false, total}, fn {:status, ^ref, status}, acc -> IO.puts("Status: #{status}") acc {:headers, ^ref, headers}, acc -> content_length = Enum.find_value(headers, fn {"content-length", len} -> len _ -> nil end) IO.puts("Content-Length: #{content_length || "unknown"}") acc {:data, ^ref, data}, {_, bytes} -> IO.write("\rDownloaded: #{bytes + byte_size(data)} bytes") IO.binwrite(file, data) {false, bytes + byte_size(data)} {:done, ^ref}, {_, bytes} -> {true, bytes} _other, acc -> acc end) end end # Usage StreamingDownload.download_file( "https://example.com/large-file.zip", "/tmp/download.zip" ) ``` -------------------------------- ### Type-Safe HTTP Request Builder with Mint Types Source: https://context7.com/context7/hexdocs_pm_mint_v1_7_1/llms.txt Defines types and a function for building HTTP requests in a type-safe manner using Mint's type definitions. It includes validation for HTTP methods and normalization for headers and paths. ```elixir defmodule RequestBuilder do @type method :: String.t() @type headers :: [{String.t(), String.t()}] @type body :: iodata() | nil | :stream @spec build_request(method(), String.t(), headers(), body()) :: {:ok, method(), String.t(), headers(), body()} | {:error, term()} def build_request(method, path, headers, body) do # Validate method unless method in ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"] do {:error, :invalid_method} end # Normalize headers headers = Enum.map(headers, fn {k, v} -> {String.downcase(k), to_string(v)} end) # Validate path path = if String.starts_with?(path, "/"), do: path, else: "/#{path}" {:ok, method, path, headers, body} end end ``` -------------------------------- ### Mint Types for HTTP Response Handling Source: https://context7.com/context7/hexdocs_pm_mint_v1_7_1/llms.txt Implements response handling using Mint's type definitions, including collecting status, headers, and body data from an HTTP stream. It also incorporates a timeout mechanism for the response collection process. ```elixir defmodule ResponseHandler do @type status :: non_neg_integer() @type response :: %{ status: status(), headers: [{String.t(), String.t()}], body: binary() } @spec collect_response(Mint.HTTP.t(), reference()) :: {:ok, response()} | {:error, term()} def collect_response(conn, request_ref) do collect_response(conn, request_ref, %{status: nil, headers: [], body: ""}) end defp collect_response(conn, ref, acc) do receive do message -> case Mint.HTTP.stream(conn, message) do {:ok, conn, responses} -> acc = process_responses(responses, ref, acc) if acc[:done] do {:ok, Map.delete(acc, :done)} else collect_response(conn, ref, acc) end {:error, _conn, error, _responses} -> {:error, error} end after 5000 -> {:error, :timeout} end end defp process_responses(responses, ref, acc) do Enum.reduce(responses, acc, fn {:status, ^ref, status}, acc -> %{acc | status: status} {:headers, ^ref, headers}, acc -> %{acc | headers: acc.headers ++ headers} {:data, ^ref, data}, acc -> %{acc | body: acc.body <> data} {:done, ^ref}, acc -> Map.put(acc, :done, true) _other, acc -> acc end) end end ``` -------------------------------- ### Handle Mint Transport Errors in HTTP Client Source: https://context7.com/context7/hexdocs_pm_mint_v1_7_1/llms.txt Provides a comprehensive error handling strategy for network transport errors when establishing an HTTP connection with Mint. It covers timeouts, DNS failures, connection refusals, SSL alerts, and closed connections. ```elixir defmodule HTTPClient do def fetch(url) do uri = URI.parse(url) case Mint.HTTP.connect( String.to_atom(uri.scheme), uri.host, uri.port || 443, transport_opts: [timeout: 5000] ) do {:ok, conn} -> handle_request(conn, uri.path || "/") {:error, %Mint.TransportError{reason: reason}} -> case reason do :timeout -> {:error, :connection_timeout} :nxdomain -> {:error, :dns_lookup_failed} :econnrefused -> {:error, :connection_refused} {:tls_alert, alert} -> {:error, {:ssl_error, alert}} :closed -> {:error, :connection_closed} other -> {:error, {:transport_error, other}} end end end defp handle_request(conn, path) do {:ok, conn, ref} = Mint.HTTP.request(conn, "GET", path, [], "") {:ok, {conn, ref}} end end ``` -------------------------------- ### Mint.HTTPError - HTTP Protocol Errors Source: https://context7.com/context7/hexdocs_pm_mint_v1_7_1/llms.txt Defines structured error types for various HTTP-level errors encountered during communication. ```APIDOC ## Mint.HTTPError - HTTP Protocol Errors ### Description This module defines the error types returned by Mint when HTTP-level issues occur, such as protocol violations, invalid responses, or connection problems. ### Method N/A (This is an error module, not an API for making requests) ### Endpoint N/A ### Parameters None ### Request Example N/A ### Response #### Success Response (200) N/A #### Response Example Errors are typically returned as the first element in a tuple, e.g., `{:error, %Mint.HTTPError{...}}`. Possible error types include: - `:invalid_status_line`: The server returned an invalid HTTP status line. - `:invalid_header_line`: An invalid header line was received. - `:invalid_chunk_size`: An invalid chunk size was encountered during chunked transfer encoding. - `:connection_closed`: The underlying connection was closed unexpectedly. - `:ssl_error`: An error occurred during the SSL/TLS handshake or communication. ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.