ReqTor (req_tor v0.1.0)

View Source

Req plugin for routing HTTP requests through a Tor SOCKS5 proxy.

ReqTor enables anonymous HTTP requests via the Tor network, supporting both regular websites and .onion hidden services.

Installation

Add req_tor to your dependencies:

def deps do
  [
    {:req_tor, "~> 0.1.0"}
  ]
end

Prerequisites

Ensure Tor is running locally:

# macOS
brew install tor
brew services start tor

# Linux
sudo apt install tor
sudo systemctl start tor

The default SOCKS5 proxy runs on port 9050. Tor Browser uses port 9150.

Req and ReqTor

Req.get!/1 sends the request directly. ReqTor.get!/1 sends it through Tor. The request struct is still a %Req.Request{}, so Req's steps (JSON, auth, redirects, retries) keep working. attached?/1 tells the two structs apart.

Req.get!("https://example.com")

ReqTor.get!("https://check.torproject.org")
ReqTor.get!("http://example.onion/")

req = ReqTor.new(tor_port: 9150, tor_browser: true)
ReqTor.get!(req, url: "http://example.onion/")

ReqTor.attached?(req)
#=> true

ReqTor.attached?(Req.new())
#=> false

head!/2, post!/2, put!/2, patch!/2, delete!/2, and request!/2 follow the same shape. Pass tor_enabled: false to skip the proxy for one call.

req = ReqTor.new()
ReqTor.get!(req, url: "https://example.com", tor_enabled: false)

Helpers

req = ReqTor.new()
ReqTor.check_tor?(req)
#=> true

ReqTor.exit_ip(req)
#=> {:ok, "185.220.101.42"}

Options

  • :tor_host - Tor SOCKS5 proxy host (default: "127.0.0.1")
  • :tor_port - Tor SOCKS5 proxy port (default: 9050, use 9150 for Tor Browser)
  • :tor_enabled - Enable or skip Tor routing for one request (default: true)
  • :tor_insecure - TLS verification (default: :auto). :auto verifies clearnet certificates and skips verification for .onion hosts. true skips verification for every host. false verifies every host.
  • :tor_browser - Use Tor Browser's user-agent string (default: false). Helps avoid blocks on sites that filter automated requests.
  • :receive_timeout - Milliseconds to wait on the Tor socket (default: 60_000). This is Req's option. Circuit setup often needs longer than Req's usual 15 seconds.

Pass Tor options to new/1 or to any verb. Every other option is a Req option. :user_agent and an existing user-agent header are kept. :tor_browser replaces the user-agent. Redirects and Req's gzip decompression run through the same Tor connection.

Summary

Functions

Attaches Tor routing to an existing Req request.

Returns true when the request was built for Tor.

Checks whether this client exits through Tor.

Makes a DELETE request through Tor.

Makes a DELETE request through Tor and returns the response, or raises.

Returns the Tor exit node IP address.

Makes a GET request through Tor.

Makes a GET request through Tor and returns the response, or raises.

Makes a HEAD request through Tor.

Makes a HEAD request through Tor and returns the response, or raises.

Merges options into a request, attaching Tor when the request does not have it yet.

Builds a request that routes through Tor.

Makes a PATCH request through Tor.

Makes a PATCH request through Tor and returns the response, or raises.

Makes a POST request through Tor.

Makes a POST request through Tor and returns the response, or raises.

Makes a PUT request through Tor.

Makes a PUT request through Tor and returns the response, or raises.

Makes an HTTP request through Tor.

Makes an HTTP request through Tor and returns the response, or raises.

Functions

attach(request, opts \\ [])

Attaches Tor routing to an existing Req request.

new/1 builds a Tor client directly. Use attach/2 when a request already has other Req plugins or options, then run it with ReqTor.get!/2 or another ReqTor verb.

Calling attach/2 again updates options and does not add a second Tor step.

Options

  • :tor_host - Tor SOCKS5 proxy host (default: "127.0.0.1")
  • :tor_port - Tor SOCKS5 proxy port (default: 9050)
  • :tor_enabled - Enable or skip Tor routing (default: true)
  • :tor_insecure - TLS verification (default: :auto)
  • :tor_browser - Use Tor Browser's user-agent (default: false)

Examples

iex> req = Req.new() |> ReqTor.attach()
iex> req.options[:tor_host]
"127.0.0.1"

iex> req = Req.new() |> ReqTor.attach(tor_port: 9150)
iex> req.options[:tor_port]
9150

req = Req.new(base_url: "http://example.onion") |> ReqTor.attach(tor_browser: true)
ReqTor.get!(req, url: "/")

attached?(request)

@spec attached?(Req.Request.t()) :: boolean()

Returns true when the request was built for Tor.

A plain Req request returns false. tor_enabled: false does not remove the attachment; it only skips the proxy for that call.

Examples

iex> ReqTor.attached?(ReqTor.new())
true

iex> ReqTor.attached?(Req.new())
false

check_tor?(request \\ [])

@spec check_tor?(Req.Request.t() | keyword()) :: boolean()

Checks whether this client exits through Tor.

Requests https://check.torproject.org/api/ip and reads the IsTor field. Accepts a request from new/1 or the same options as new/1.

Examples

req = ReqTor.new()
ReqTor.check_tor?(req)
#=> true

ReqTor.check_tor?(tor_port: 9150)
#=> true

delete(request, options \\ [])

@spec delete(
  Req.Request.t() | keyword() | URI.t() | String.t(),
  keyword()
) :: {:ok, Req.Response.t()} | {:error, Exception.t()}

Makes a DELETE request through Tor.

Returns {:ok, response} or {:error, exception}.

The first argument is a URL, a keyword list, or a request from new/1. Tor options can be passed here or to new/1. Every other option is a Req option.

delete!(request, options \\ [])

@spec delete!(
  Req.Request.t() | keyword() | URI.t() | String.t(),
  keyword()
) :: Req.Response.t()

Makes a DELETE request through Tor and returns the response, or raises.

The first argument is a URL, a keyword list, or a request from new/1. Tor options can be passed here or to new/1. Every other option is a Req option.

exit_ip(request \\ [])

@spec exit_ip(Req.Request.t() | keyword()) ::
  {:ok, String.t() | nil} | {:error, term()}

Returns the Tor exit node IP address.

Accepts a request from new/1 or the same options as new/1.

Examples

req = ReqTor.new()
ReqTor.exit_ip(req)
#=> {:ok, "185.220.101.42"}

ReqTor.exit_ip(tor_port: 9150)
#=> {:ok, "185.220.101.42"}

get(request, options \\ [])

@spec get(
  Req.Request.t() | keyword() | URI.t() | String.t(),
  keyword()
) :: {:ok, Req.Response.t()} | {:error, Exception.t()}

Makes a GET request through Tor.

Returns {:ok, response} or {:error, exception}.

The first argument is a URL, a keyword list, or a request from new/1. Tor options can be passed here or to new/1. Every other option is a Req option.

get!(request, options \\ [])

@spec get!(
  Req.Request.t() | keyword() | URI.t() | String.t(),
  keyword()
) :: Req.Response.t()

Makes a GET request through Tor and returns the response, or raises.

The first argument is a URL, a keyword list, or a request from new/1. Tor options can be passed here or to new/1. Every other option is a Req option.

head(request, options \\ [])

@spec head(
  Req.Request.t() | keyword() | URI.t() | String.t(),
  keyword()
) :: {:ok, Req.Response.t()} | {:error, Exception.t()}

Makes a HEAD request through Tor.

Returns {:ok, response} or {:error, exception}.

The first argument is a URL, a keyword list, or a request from new/1. Tor options can be passed here or to new/1. Every other option is a Req option.

head!(request, options \\ [])

@spec head!(
  Req.Request.t() | keyword() | URI.t() | String.t(),
  keyword()
) :: Req.Response.t()

Makes a HEAD request through Tor and returns the response, or raises.

The first argument is a URL, a keyword list, or a request from new/1. Tor options can be passed here or to new/1. Every other option is a Req option.

merge(request, options)

@spec merge(
  Req.Request.t(),
  keyword()
) :: Req.Request.t()

Merges options into a request, attaching Tor when the request does not have it yet.

Examples

iex> req = ReqTor.new() |> ReqTor.merge(tor_port: 9150, base_url: "http://example.onion")
iex> req.options[:tor_port]
9150
iex> req.options[:base_url]
"http://example.onion"

new(options \\ [])

@spec new(keyword()) :: Req.Request.t()

Builds a request that routes through Tor.

Tor options are listed in the module docs. Every other option is passed to Req.new/1.

Examples

iex> req = ReqTor.new()
iex> req.options[:tor_host]
"127.0.0.1"
iex> req.options[:tor_port]
9050
iex> ReqTor.attached?(req)
true

iex> req = ReqTor.new(base_url: "http://example.onion", tor_port: 9150)
iex> req.options[:base_url]
"http://example.onion"
iex> req.options[:tor_port]
9150

patch(request, options \\ [])

@spec patch(
  Req.Request.t() | keyword() | URI.t() | String.t(),
  keyword()
) :: {:ok, Req.Response.t()} | {:error, Exception.t()}

Makes a PATCH request through Tor.

Returns {:ok, response} or {:error, exception}.

The first argument is a URL, a keyword list, or a request from new/1. Tor options can be passed here or to new/1. Every other option is a Req option.

patch!(request, options \\ [])

@spec patch!(
  Req.Request.t() | keyword() | URI.t() | String.t(),
  keyword()
) :: Req.Response.t()

Makes a PATCH request through Tor and returns the response, or raises.

The first argument is a URL, a keyword list, or a request from new/1. Tor options can be passed here or to new/1. Every other option is a Req option.

post(request, options \\ [])

@spec post(
  Req.Request.t() | keyword() | URI.t() | String.t(),
  keyword()
) :: {:ok, Req.Response.t()} | {:error, Exception.t()}

Makes a POST request through Tor.

Returns {:ok, response} or {:error, exception}.

The first argument is a URL, a keyword list, or a request from new/1. Tor options can be passed here or to new/1. Every other option is a Req option.

post!(request, options \\ [])

@spec post!(
  Req.Request.t() | keyword() | URI.t() | String.t(),
  keyword()
) :: Req.Response.t()

Makes a POST request through Tor and returns the response, or raises.

The first argument is a URL, a keyword list, or a request from new/1. Tor options can be passed here or to new/1. Every other option is a Req option.

put(request, options \\ [])

@spec put(
  Req.Request.t() | keyword() | URI.t() | String.t(),
  keyword()
) :: {:ok, Req.Response.t()} | {:error, Exception.t()}

Makes a PUT request through Tor.

Returns {:ok, response} or {:error, exception}.

The first argument is a URL, a keyword list, or a request from new/1. Tor options can be passed here or to new/1. Every other option is a Req option.

put!(request, options \\ [])

@spec put!(
  Req.Request.t() | keyword() | URI.t() | String.t(),
  keyword()
) :: Req.Response.t()

Makes a PUT request through Tor and returns the response, or raises.

The first argument is a URL, a keyword list, or a request from new/1. Tor options can be passed here or to new/1. Every other option is a Req option.

request(request, options \\ [])

@spec request(
  Req.Request.t() | keyword(),
  keyword()
) :: {:ok, Req.Response.t()} | {:error, Exception.t()}

Makes an HTTP request through Tor.

Returns {:ok, response} or {:error, exception}. Set :method in the options or on the request. It defaults to the request's method.

The first argument is a keyword list or a request from new/1.

request!(request, options \\ [])

@spec request!(
  Req.Request.t() | keyword(),
  keyword()
) :: Req.Response.t()

Makes an HTTP request through Tor and returns the response, or raises.

See request/2.