Class: Html2rss::RequestService::Policy

Inherits:
Object
  • Object
show all
Defined in:
lib/html2rss/request_service/policy.rb

Overview

Enforcer for the runtime request envelope of a single feed build.

Public façade for request limits, same-origin/follow-up rules, and network reachability. DNS/host/IP enforcement lives on NetworkGuard.

Constant Summary collapse

MAX_REQUESTS_CEILING =

Hard ceiling for configured max_requests during a feed build.

10
DEFAULTS =

Default policy values used when request controls are not explicitly set.

{
  connect_timeout_seconds: Integer(ENV.fetch('HTML2RSS_CONNECT_TIMEOUT_SECONDS', 5)),
  read_timeout_seconds: Integer(ENV.fetch('HTML2RSS_READ_TIMEOUT_SECONDS', 10)),
  total_timeout_seconds: Integer(ENV.fetch('HTML2RSS_TOTAL_TIMEOUT_SECONDS', 30)),
  max_redirects: 3,
  max_response_bytes: 5_242_880,
  max_decompressed_bytes: 10_485_760,
  max_requests: 1,
  allow_private_networks: false,
  allow_cross_origin_followups: false
}.freeze
DEFAULT_POLICY =

Shared immutable policy instance used for default request execution.

Policy.new

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(connect_timeout_seconds: DEFAULTS[:connect_timeout_seconds], read_timeout_seconds: DEFAULTS[:read_timeout_seconds], total_timeout_seconds: DEFAULTS[:total_timeout_seconds], max_redirects: DEFAULTS[:max_redirects], max_response_bytes: DEFAULTS[:max_response_bytes], max_decompressed_bytes: DEFAULTS[:max_decompressed_bytes], max_requests: DEFAULTS[:max_requests], allow_private_networks: DEFAULTS[:allow_private_networks], allow_cross_origin_followups: DEFAULTS[:allow_cross_origin_followups], resolver: Socket) ⇒ Policy

Returns a new instance of Policy.

Parameters:

  • connect_timeout_seconds (Integer) (defaults to: DEFAULTS[:connect_timeout_seconds])

    maximum connection setup time

  • read_timeout_seconds (Integer) (defaults to: DEFAULTS[:read_timeout_seconds])

    maximum read stall time

  • total_timeout_seconds (Integer) (defaults to: DEFAULTS[:total_timeout_seconds])

    maximum total request time

  • max_redirects (Integer) (defaults to: DEFAULTS[:max_redirects])

    maximum redirect count

  • max_response_bytes (Integer) (defaults to: DEFAULTS[:max_response_bytes])

    maximum streamed response bytes

  • max_decompressed_bytes (Integer) (defaults to: DEFAULTS[:max_decompressed_bytes])

    maximum final body size

  • max_requests (Integer) (defaults to: DEFAULTS[:max_requests])

    maximum requests per feed build

  • allow_private_networks (Boolean) (defaults to: DEFAULTS[:allow_private_networks])

    whether private network targets are allowed

  • allow_cross_origin_followups (Boolean) (defaults to: DEFAULTS[:allow_cross_origin_followups])

    whether follow-up requests may leave the origin host

  • resolver (#each_address) (defaults to: Socket)

    DNS resolver used for hostname classification



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/html2rss/request_service/policy.rb', line 40

def initialize(connect_timeout_seconds: DEFAULTS[:connect_timeout_seconds], # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
               read_timeout_seconds: DEFAULTS[:read_timeout_seconds],
               total_timeout_seconds: DEFAULTS[:total_timeout_seconds],
               max_redirects: DEFAULTS[:max_redirects],
               max_response_bytes: DEFAULTS[:max_response_bytes],
               max_decompressed_bytes: DEFAULTS[:max_decompressed_bytes],
               max_requests: DEFAULTS[:max_requests],
               allow_private_networks: DEFAULTS[:allow_private_networks],
               allow_cross_origin_followups: DEFAULTS[:allow_cross_origin_followups],
               resolver: Socket)
  @connect_timeout_seconds = validate_positive_integer!(:connect_timeout_seconds, connect_timeout_seconds)
  @read_timeout_seconds = validate_positive_integer!(:read_timeout_seconds, read_timeout_seconds)
  @total_timeout_seconds = validate_positive_integer!(:total_timeout_seconds, total_timeout_seconds)
  @max_redirects = validate_non_negative_integer!(:max_redirects, max_redirects)
  @max_response_bytes = validate_positive_integer!(:max_response_bytes, max_response_bytes)
  @max_decompressed_bytes = validate_positive_integer!(:max_decompressed_bytes, max_decompressed_bytes)
  @max_requests = [validate_positive_integer!(:max_requests, max_requests), MAX_REQUESTS_CEILING].min
  @allow_private_networks = allow_private_networks ? true : false
  @allow_cross_origin_followups = allow_cross_origin_followups ? true : false
  @network_guard = NetworkGuard.new(allow_private_networks: @allow_private_networks, resolver:)
  freeze
end

Instance Attribute Details

#connect_timeout_secondsObject (readonly)

Returns the value of attribute connect_timeout_seconds.



63
64
65
# File 'lib/html2rss/request_service/policy.rb', line 63

def connect_timeout_seconds
  @connect_timeout_seconds
end

#max_decompressed_bytesObject (readonly)

Returns the value of attribute max_decompressed_bytes.



63
64
65
# File 'lib/html2rss/request_service/policy.rb', line 63

def max_decompressed_bytes
  @max_decompressed_bytes
end

#max_redirectsObject (readonly)

Returns the value of attribute max_redirects.



63
64
65
# File 'lib/html2rss/request_service/policy.rb', line 63

def max_redirects
  @max_redirects
end

#max_requestsObject (readonly)

Returns the value of attribute max_requests.



63
64
65
# File 'lib/html2rss/request_service/policy.rb', line 63

def max_requests
  @max_requests
end

#max_response_bytesObject (readonly)

Returns the value of attribute max_response_bytes.



63
64
65
# File 'lib/html2rss/request_service/policy.rb', line 63

def max_response_bytes
  @max_response_bytes
end

#read_timeout_secondsObject (readonly)

Returns the value of attribute read_timeout_seconds.



63
64
65
# File 'lib/html2rss/request_service/policy.rb', line 63

def read_timeout_seconds
  @read_timeout_seconds
end

#total_timeout_secondsObject (readonly)

Returns the value of attribute total_timeout_seconds.



63
64
65
# File 'lib/html2rss/request_service/policy.rb', line 63

def total_timeout_seconds
  @total_timeout_seconds
end

Class Method Details

.defaultPolicy

Returns the default request policy.

rubocop:disable Layout/ClassStructure

Returns:

  • (Policy)

    a default, frozen policy instance



88
89
90
# File 'lib/html2rss/request_service/policy.rb', line 88

def self.default
  new
end

Instance Method Details

#allow_cross_origin_followups?Boolean

Returns whether follow-up requests may leave the initial origin.

Returns:

  • (Boolean)

    whether follow-up requests may leave the initial origin



79
80
81
# File 'lib/html2rss/request_service/policy.rb', line 79

def allow_cross_origin_followups?
  @allow_cross_origin_followups
end

#allow_private_networks?Boolean

Returns whether private network targets may be requested.

Returns:

  • (Boolean)

    whether private network targets may be requested



73
74
75
# File 'lib/html2rss/request_service/policy.rb', line 73

def allow_private_networks?
  @allow_private_networks
end

#validate_redirect!(from_url:, to_url:, origin_url:, relation:) ⇒ void

This method returns an undefined value.

Validates a redirect hop before it is followed.

Parameters:

  • from_url (Html2rss::Url)

    URL that produced the redirect

  • to_url (Html2rss::Url)

    redirect destination

  • origin_url (Html2rss::Url)

    initial URL of the feed build

  • relation (Symbol)

    logical reason for the request

Raises:



116
117
118
119
120
121
122
# File 'lib/html2rss/request_service/policy.rb', line 116

def validate_redirect!(from_url:, to_url:, origin_url:, relation:)
  if from_url.scheme == 'https' && to_url.scheme == 'http'
    raise UnsupportedUrlScheme, 'Redirect downgraded from https to http'
  end

  validate_request!(url: to_url, origin_url:, relation:)
end

#validate_remote_ip!(ip:, url:) ⇒ void

This method returns an undefined value.

Validates the resolved remote IP for a completed request.

Parameters:

  • ip (String, nil)

    remote IP address reported by the client

  • url (Html2rss::Url)

    URL associated with the response

Raises:



131
132
133
# File 'lib/html2rss/request_service/policy.rb', line 131

def validate_remote_ip!(ip:, url:)
  network_guard.validate_remote_ip!(ip:, url:)
end

#validate_request!(url:, origin_url:, relation:) ⇒ void

This method returns an undefined value.

Validates whether a request target is permitted for the given context.

Parameters:

  • url (Html2rss::Url)

    destination URL

  • origin_url (Html2rss::Url)

    initial URL of the feed build

  • relation (Symbol)

    logical reason for the request

Raises:



102
103
104
105
# File 'lib/html2rss/request_service/policy.rb', line 102

def validate_request!(url:, origin_url:, relation:)
  enforce_same_origin!(url, origin_url, relation)
  network_guard.enforce_public_network!(url)
end