Class: Dommy::Navigation::Fetcher

Inherits:
Object
  • Object
show all
Defined in:
lib/dommy/navigation.rb

Overview

URL resolution + redirect following over a Resources adapter. This is the engine-agnostic core of "fetch the document at this navigation target": it folds GET params into the query, serializes a POST body, follows 3xx redirects (303→GET, 301/302 POST→GET, 307/308 keep method+body), and returns [Resources::Response | nil, final_url]. The Rack session's own redirect loop is the reference; this is its Resources-based generalization so the core Browser can navigate without Rack.

Constant Summary collapse

REDIRECT_STATUSES =
[301, 302, 303, 307, 308].freeze
KEEP_METHOD_STATUSES =
[307, 308].freeze
QUERY_METHODS =
%w[GET HEAD].freeze

Instance Method Summary collapse

Constructor Details

#initialize(resources, max_redirects: 20, same_origin: false) ⇒ Fetcher

same_origin: true scopes navigation to a single origin (the first one requested): a target or redirect to a different scheme/host/port is blocked (returns no response, so the browser stays put) — a test policy mirroring dommy-rack, so a page can't wander off to an external site the resources adapter happens to serve.



84
85
86
87
88
89
# File 'lib/dommy/navigation.rb', line 84

def initialize(resources, max_redirects: 20, same_origin: false)
  @resources = resources
  @max_redirects = max_redirects
  @same_origin = same_origin
  @origin = nil
end

Instance Method Details

#append_query(url, params) ⇒ Object

Merge ordered [name, value] params into url's query, keeping any existing query and a trailing fragment (browser address-bar form).



117
118
119
120
121
122
123
124
# File 'lib/dommy/navigation.rb', line 117

def append_query(url, params)
  encoded = URI.encode_www_form(params)
  return url if encoded.empty?

  base, hash, fragment = url.to_s.partition("#")
  sep = base.include?("?") ? "&" : "?"
  "#{base}#{sep}#{encoded}#{hash}#{fragment}"
end

#request(method:, url:, params: nil, body: nil, enctype: nil, headers: {}) ⇒ Object

Resolve + issue the request, following redirects. params is an ordered [name, value] array (form data set); it folds into the query for GET/HEAD and becomes the request body otherwise. Returns [response|nil, final_url]; a nil response means "nothing served this URL" (stay on the current page).



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/dommy/navigation.rb', line 95

def request(method:, url:, params: nil, body: nil, enctype: nil, headers: {})
  return [nil, url.to_s] unless @resources

  verb = method.to_s.upcase
  target = url.to_s
  # The first request establishes the origin the session is scoped to.
  @origin ||= origin_of(target) if @same_origin
  return [nil, target] if cross_origin?(target)

  hdrs = headers.dup
  if params && QUERY_METHODS.include?(verb)
    target = append_query(target, params)
    params = nil
  elsif params
    body, content_type = encode_body(params, enctype)
    hdrs["Content-Type"] ||= content_type if content_type
  end
  run(verb, target, body, hdrs, 0)
end