Module: HttpMimic::Obscura

Defined in:
lib/http_mimic/obscura.rb

Defined Under Namespace

Classes: BinaryNotFoundError, ExecutionError

Class Method Summary collapse

Class Method Details

.render(url, options = {}) ⇒ HttpMimic::Response

Renders a web page using the Obscura headless SPA engine and returns an HttpMimic::Response

Parameters:

  • url (String, URI)

    The target URL to render

  • options (Hash) (defaults to: {})

    Rendering options

Options Hash (options):

  • :wait_until (String) — default: 'networkidle0'

    Wait condition (e.g. 'load', 'domcontentloaded', 'networkidle0')

  • :timeout (Integer)

    Per-command timeout in seconds

  • :proxy (String)

    Proxy URL (e.g. 'socks5://127.0.0.1:1080' or 'http://...')

  • :eval (String)

    JavaScript expression to evaluate on the page

  • :dump (String)

    Output format ('html', 'text', 'links')

Returns:

Raises:



23
24
25
26
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/http_mimic/obscura.rb', line 23

def render(url, options = {})
  bin = resolve_binary
  raise BinaryNotFoundError, "Obscura binary not found. Run HttpMimic.download_obscura! or configure obscura_path." unless bin

  args = [bin, 'fetch', url.to_s]

  # Dump format: default to html (can be 'html', 'text', 'links', 'markdown', 'original', 'cookies')
  dump_format = options[:dump] || 'html'
  args << '--dump' << dump_format.to_s

  # Stealth mode (enabled by default)
  args << '--stealth' if options.fetch(:stealth, true)

  # Wait condition
  if options[:wait_until]
    args << '--wait-until' << options[:wait_until].to_s
  end

  # Wait delay
  if options[:wait]
    args << '--wait' << options[:wait].to_i.to_s
  end

  # Timeout in seconds (integer)
  timeout = options[:timeout] || HttpMimic.configuration.default_timeout
  args << '--timeout' << timeout.to_i.to_s if timeout

  # User Agent
  if options[:user_agent]
    args << '--user-agent' << options[:user_agent].to_s
  end

  # Proxy
  if options[:proxy]
    args << '--proxy' << options[:proxy].to_s
  end

  # Cookies: pass cookies string or hash (Two-Phase Pipeline support)
  if options[:cookies] || options[:cookie]
    cookie_val = options[:cookies] || options[:cookie]
    cookie_str = if cookie_val.is_a?(Hash)
      cookie_val.map { |k, v| "#{k}=#{v}" }.join('; ')
    elsif cookie_val.is_a?(Array)
      cookie_val.join('; ')
    else
      cookie_val.to_s
    end
    args << '--cookie' << cookie_str unless cookie_str.empty?
  end

  # Optional JS evaluation
  args << '--eval' << options[:eval].to_s if options[:eval]

  # Selector
  args << '--selector' << options[:selector].to_s if options[:selector]

  log_debug("Executing Obscura SPA render: #{args.join(' ')}")

  stdout, stderr, status = Open3.capture3(*args)

  code = (status && status.success?) ? 200 : (status ? status.exitstatus : 500)

  raw_headers = "HTTP/2 200 OK\r\ncontent-type: text/html; charset=utf-8\r\nx-rendered-by: obscura\r\n\r\n"
  headers = Headers.new({ 'content-type' => 'text/html; charset=utf-8', 'x-rendered-by' => 'obscura' })

  Response.new(
    code: code,
    http_version: 'HTTP/2',
    status_message: status&.success? ? 'OK (Obscura SPA Rendered)' : 'Error',
    headers: headers,
    cookies: Cookies.new,
    body: stdout,
    parsed_response: nil,
    raw_headers: raw_headers,
    history: [],
    request_url: url.to_s,
    stderr: stderr,
    exit_code: status ? status.exitstatus : 0,
    command: args.join(' ')
  )
end

.resolve_binaryObject



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/http_mimic/obscura.rb', line 105

def resolve_binary
  # 1. Configured custom path
  custom_path = HttpMimic.configuration.obscura_path
  if custom_path && (File.file?(custom_path) || File.executable?(custom_path))
    return custom_path
  end

  # 2. Check install directory or system PATH
  installed_path = Downloader.obscura_path
  return installed_path if installed_path

  # 3. Auto-download if enabled
  if HttpMimic.configuration.auto_download
    begin
      return Downloader.download_obscura!
    rescue StandardError => e
      HttpMimic.configuration.logger&.warn("[HttpMimic::Obscura] Auto-download failed: #{e.message}")
    end
  end

  nil
end