Module: RubyLLM::Toolbox::Tools::HttpHelpers

Included in:
DownloadFile, HttpRequest, WebFetch
Defined in:
lib/ruby_llm/toolbox/tools/http_helpers.rb

Overview

Shared guarded-HTTP behavior for web_fetch and http_request. With the guard on (the default), every request URL and every redirect hop passes through UrlGuard, and the socket is pinned to the vetted IP so a second DNS lookup can’t redirect us to an internal address. With guard: false (only reachable via an operator-permitted unsafe override), the URL is fetched directly with no SSRF checks.

Defined Under Namespace

Classes: FetchError, Response

Instance Method Summary collapse

Instance Method Details

#guarded_get(url, guard: true) ⇒ Object

GET with redirect following and a body-size cap. Each hop is re-guarded and re-pinned when guard is true.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/ruby_llm/toolbox/tools/http_helpers.rb', line 27

def guarded_get(url, guard: true)
  checker = url_guard if guard
  current = url
  seen = 0

  loop do
    uri, pin = resolve_hop(checker, current, guard)
    response, body = perform(uri, "GET", nil, nil, pin)

    if redirect?(response)
      location = response["location"]
      raise FetchError, "redirect with no Location header (status #{response.code})" unless location
      raise FetchError, "too many redirects (max #{config.max_redirects})" if seen >= config.max_redirects

      seen += 1
      current = URI.join(uri.to_s, location).to_s
      next
    end

    return Response.new(status: response.code.to_i, headers: response.to_hash,
                        body: body, final_url: uri.to_s)
  end
rescue Safety::UrlGuard::Blocked
  raise
rescue StandardError => e
  raise FetchError, e.message
end

#guarded_request(method, url, headers: {}, body: nil, guard: true) ⇒ Object

A single request with an explicit method/headers/body (no redirect following). Used by http_request.



57
58
59
60
61
62
63
64
65
# File 'lib/ruby_llm/toolbox/tools/http_helpers.rb', line 57

def guarded_request(method, url, headers: {}, body: nil, guard: true)
  uri, pin = resolve_hop(url_guard, url, guard)
  response, raw = perform(uri, method.to_s.upcase, headers, body, pin)
  Response.new(status: response.code.to_i, headers: response.to_hash, body: raw, final_url: uri.to_s)
rescue Safety::UrlGuard::Blocked
  raise
rescue StandardError => e
  raise FetchError, e.message
end

#url_guardObject



21
22
23
# File 'lib/ruby_llm/toolbox/tools/http_helpers.rb', line 21

def url_guard
  Safety::UrlGuard.new(allowlist: config.web_allowlist, denylist: config.web_denylist)
end