Module: Unmagic::Browser::Failures

Defined in:
lib/unmagic/browser/failures.rb

Overview

The one place a Puppeteer, socket or HTTP failure becomes an Error.

Callers classify failures by rescuing, so every path out of this gem has to raise one of our types — including the ones that come out of puppeteer-ruby, three gems deep, as a bare Puppeteer::Error.

Constant Summary collapse

UNREACHABLE_PATTERN =

Chrome reports a failed navigation as a net::ERR_* protocol message rather than as a distinct error class, so the message is the only signal separating "couldn't reach the page" from "the browser broke".

/net::ERR_|ERR_NAME_NOT_RESOLVED|ERR_CONNECTION|ERR_ADDRESS|ERR_INTERNET_DISCONNECTED/
TRANSPORT_ERRORS =

Anything that means the socket underneath went away.

[
  IOError,
  SocketError,
  Errno::ECONNRESET,
  Errno::ECONNREFUSED,
  Errno::EPIPE,
  Errno::EHOSTUNREACH,
  Errno::ENETUNREACH,
  Errno::ETIMEDOUT,
].freeze

Class Method Summary collapse

Class Method Details

.rate_limited?(error) ⇒ Boolean

A rejected WebSocket handshake carries its HTTP status on the exception rather than in the class, and 429 there means a quota — worth retrying on a cooldown — not a dead network.

Parameters:

  • error (Exception)

Returns:

  • (Boolean)

    whether the error is a quota rejection



71
72
73
# File 'lib/unmagic/browser/failures.rb', line 71

def rate_limited?(error)
  error.respond_to?(:response) && error.response.respond_to?(:status) && error.response&.status == 429
end

.unreachable?(error) ⇒ Boolean

Returns whether the error is Chrome refusing to load a URL.

Parameters:

  • error (Exception)

Returns:

  • (Boolean)

    whether the error is Chrome refusing to load a URL



61
62
63
# File 'lib/unmagic/browser/failures.rb', line 61

def unreachable?(error)
  UNREACHABLE_PATTERN.match?(error.message.to_s)
end

.wrap(doing) { ... } ⇒ Object

Run block, re-raising anything it throws as a typed error.

Parameters:

  • doing (String)

    what was being attempted, for the message

Yields:

  • the work to guard

Returns:

  • (Object)

    whatever the block returns

Raises:



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/unmagic/browser/failures.rb', line 40

def wrap(doing)
  yield
rescue Error
  # Already one of ours — a locator that gave up, say. Leave it alone.
  raise
rescue Puppeteer::TimeoutError => error
  raise Error::TimedOut, "Timed out #{doing}: #{error.message}"
rescue *TRANSPORT_ERRORS => error
  raise Error::Unreachable, "Couldn't reach the browser while #{doing}: #{error.message}"
rescue Puppeteer::Error => error
  raise Error::Unreachable, "Couldn't reach the page while #{doing}: #{error.message}" if unreachable?(error)

  raise Error::RenderFailed, "The browser failed while #{doing}: #{error.message}"
rescue StandardError => error
  raise Error::RateLimited, "Hit a rate or concurrency limit while #{doing}." if rate_limited?(error)

  raise
end