Class: Puppeteer::HTTPRequest

Inherits:
Object
  • Object
show all
Includes:
DebugPrint, IfPresent
Defined in:
lib/puppeteer/http_request.rb

Defined Under Namespace

Classes: AlreadyHandledError, InterceptResolutionState, InterceptionNotEnabledError, InternalAccessor

Constant Summary collapse

DEFAULT_INTERCEPT_RESOLUTION_PRIORITY =
0
ERROR_REASONS =
{
  'aborted' => 'Aborted',
  'accessdenied' => 'AccessDenied',
  'addressunreachable' => 'AddressUnreachable',
  'blockedbyclient' => 'BlockedByClient',
  'blockedbyresponse' => 'BlockedByResponse',
  'connectionaborted' => 'ConnectionAborted',
  'connectionclosed' => 'ConnectionClosed',
  'connectionfailed' => 'ConnectionFailed',
  'connectionrefused' => 'ConnectionRefused',
  'connectionreset' => 'ConnectionReset',
  'internetdisconnected' => 'InternetDisconnected',
  'namenotresolved' => 'NameNotResolved',
  'timedout' => 'TimedOut',
  'failed' => 'Failed',
}.freeze
STATUS_TEXTS =

List taken from www.iana.org/assignments/http-status-codes/http-status-codes.xhtml with extra 306 and 418 codes.

{
  '100' => 'Continue',
  '101' => 'Switching Protocols',
  '102' => 'Processing',
  '103' => 'Early Hints',
  '200' => 'OK',
  '201' => 'Created',
  '202' => 'Accepted',
  '203' => 'Non-Authoritative Information',
  '204' => 'No Content',
  '205' => 'Reset Content',
  '206' => 'Partial Content',
  '207' => 'Multi-Status',
  '208' => 'Already Reported',
  '226' => 'IM Used',
  '300' => 'Multiple Choices',
  '301' => 'Moved Permanently',
  '302' => 'Found',
  '303' => 'See Other',
  '304' => 'Not Modified',
  '305' => 'Use Proxy',
  '306' => 'Switch Proxy',
  '307' => 'Temporary Redirect',
  '308' => 'Permanent Redirect',
  '400' => 'Bad Request',
  '401' => 'Unauthorized',
  '402' => 'Payment Required',
  '403' => 'Forbidden',
  '404' => 'Not Found',
  '405' => 'Method Not Allowed',
  '406' => 'Not Acceptable',
  '407' => 'Proxy Authentication Required',
  '408' => 'Request Timeout',
  '409' => 'Conflict',
  '410' => 'Gone',
  '411' => 'Length Required',
  '412' => 'Precondition Failed',
  '413' => 'Payload Too Large',
  '414' => 'URI Too Long',
  '415' => 'Unsupported Media Type',
  '416' => 'Range Not Satisfiable',
  '417' => 'Expectation Failed',
  '418' => 'I\'m a teapot',
  '421' => 'Misdirected Request',
  '422' => 'Unprocessable Entity',
  '423' => 'Locked',
  '424' => 'Failed Dependency',
  '425' => 'Too Early',
  '426' => 'Upgrade Required',
  '428' => 'Precondition Required',
  '429' => 'Too Many Requests',
  '431' => 'Request Header Fields Too Large',
  '451' => 'Unavailable For Legal Reasons',
  '500' => 'Internal Server Error',
  '501' => 'Not Implemented',
  '502' => 'Bad Gateway',
  '503' => 'Service Unavailable',
  '504' => 'Gateway Timeout',
  '505' => 'HTTP Version Not Supported',
  '506' => 'Variant Also Negotiates',
  '507' => 'Insufficient Storage',
  '508' => 'Loop Detected',
  '510' => 'Not Extended',
  '511' => 'Network Authentication Required',
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from IfPresent

#if_present

Methods included from DebugPrint

#debug_print, #debug_puts

Constructor Details

#initialize(client, frame, interception_id, allow_interception, event, redirect_chain) ⇒ HTTPRequest

Returns a new instance of HTTPRequest.

Parameters:



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/puppeteer/http_request.rb', line 94

def initialize(client, frame, interception_id, allow_interception, event, redirect_chain)
  @client = client
  @request_id = event['requestId']
  @is_navigation_request = event['requestId'] == event['loaderId'] && event['type'] == 'Document'
  @interception_id = interception_id
  @allow_interception = allow_interception
  @url = event['request']['url']
  url_fragment = event.dig('request', 'urlFragment')
  @url += url_fragment if url_fragment
  resource_type = event['type'] || event['resourceType'] || 'other'
  @resource_type = resource_type.downcase
  @method = event['request']['method']
  @post_data = parse_post_data(event)
  has_post_data = event.dig('request', 'hasPostData')
  @has_post_data = has_post_data.nil? ? !@post_data.nil? : has_post_data
  @frame = frame
  @redirect_chain = redirect_chain
  @continue_request_overrides = {}
  @intercept_resolution_state = InterceptResolutionState.none
  @interception_handled = false
  @intercept_handlers = []
  @initiator = event['initiator']

  @headers = {}
  update_headers(event.dig('request', 'headers') || {})
  @from_memory_cache = false

  @internal = InternalAccessor.new(self)
end

Instance Attribute Details

#clientObject (readonly)

Returns the value of attribute client.



125
126
127
# File 'lib/puppeteer/http_request.rb', line 125

def client
  @client
end

#frameObject (readonly)

Returns the value of attribute frame.



125
126
127
# File 'lib/puppeteer/http_request.rb', line 125

def frame
  @frame
end

#initiatorObject (readonly)

Returns the value of attribute initiator.



125
126
127
# File 'lib/puppeteer/http_request.rb', line 125

def initiator
  @initiator
end

#internalObject (readonly)

Returns the value of attribute internal.



124
125
126
# File 'lib/puppeteer/http_request.rb', line 124

def internal
  @internal
end

#methodObject (readonly)

Returns the value of attribute method.



125
126
127
# File 'lib/puppeteer/http_request.rb', line 125

def method
  @method
end

#post_dataObject (readonly)

Returns the value of attribute post_data.



125
126
127
# File 'lib/puppeteer/http_request.rb', line 125

def post_data
  @post_data
end

#resource_typeObject (readonly)

Returns the value of attribute resource_type.



125
126
127
# File 'lib/puppeteer/http_request.rb', line 125

def resource_type
  @resource_type
end

#responseObject (readonly)

Returns the value of attribute response.



125
126
127
# File 'lib/puppeteer/http_request.rb', line 125

def response
  @response
end

#urlObject (readonly)

Returns the value of attribute url.



125
126
127
# File 'lib/puppeteer/http_request.rb', line 125

def url
  @url
end

Instance Method Details

#abort(error_code: :failed, priority: nil) ⇒ Object

abort request on request interception.

Example:

page.on 'request' do |req|
  if req.url.include?("porn")
    req.abort
  else
    req.continue
  end
end

Parameters:

  • error_code (String|Symbol) (defaults to: :failed)


480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
# File 'lib/puppeteer/http_request.rb', line 480

def abort(error_code: :failed, priority: nil)
  error_reason = ERROR_REASONS[error_code.to_s]
  unless error_reason
    raise ArgumentError.new("Unknown error code: #{error_code}")
  end
  assert_interception_allowed
  assert_interception_not_handled
  return unless can_be_intercepted?

  if priority.nil?
    return abort_impl(error_reason)
  end
  @abort_error_reason = error_reason

  if @intercept_resolution_state.priority_unspecified? || priority >= @intercept_resolution_state.priority
    @intercept_resolution_state = InterceptResolutionState.abort(priority: priority)
  end
end

#abort_error_reasonObject



203
204
205
206
# File 'lib/puppeteer/http_request.rb', line 203

def abort_error_reason
  assert_interception_allowed
  @abort_error_reason
end

#continue(url: nil, method: nil, post_data: nil, headers: nil, priority: nil) ⇒ Object

proceed request on request interception.

Example:

page.on 'request' do |req|
  # Override headers
  headers = req.headers.merge(
    foo: 'bar', # set "foo" header
    origin: nil, # remove "origin" header
  )
  req.continue(headers: headers)
end

Parameters:

  • error_code (String|Symbol)


316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/puppeteer/http_request.rb', line 316

def continue(url: nil, method: nil, post_data: nil, headers: nil, priority: nil)
  assert_interception_allowed
  assert_interception_not_handled
  return unless can_be_intercepted?

  overrides = {
    url: url,
    method: method,
    post_data: post_data,
    headers: headers,
  }.compact

  if priority.nil?
    continue_impl(overrides)
    return
  end

  @continue_request_overrides = overrides
  if @intercept_resolution_state.priority_unspecified? || priority > @intercept_resolution_state.priority
    @intercept_resolution_state = InterceptResolutionState.continue(priority: priority)
    return
  end

  if priority == @intercept_resolution_state.priority
    if @intercept_resolution_state.action == 'abort' || @intercept_resolution_state.action == 'respond'
      return
    end
    @intercept_resolution_state = InterceptResolutionState.continue(priority: priority)
  end
end

#continue_request_overridesObject

if the interception is allowed to continue (ie, ‘abort()` and `respond()` aren’t called).



190
191
192
193
# File 'lib/puppeteer/http_request.rb', line 190

def continue_request_overrides
  assert_interception_allowed
  @continue_request_overrides
end

#enqueue_intercept_action(pending_handler) ⇒ Object

Adds an async request handler to the processing queue. Deferred handlers are not guaranteed to execute in any particular order, but they are guarnateed to resolve before the request interception is finalized.

Parameters:

  • pending_handler (Proc)


237
238
239
# File 'lib/puppeteer/http_request.rb', line 237

def enqueue_intercept_action(pending_handler)
  @intercept_handlers << pending_handler
end

#failureObject



267
268
269
270
271
# File 'lib/puppeteer/http_request.rb', line 267

def failure
  if_present(@failure_text) do |failure_text|
    { errorText: @failure_text }
  end
end

#fetch_post_dataObject



143
144
145
146
147
148
149
# File 'lib/puppeteer/http_request.rb', line 143

def fetch_post_data
  response = @client.send_message('Network.getRequestPostData', requestId: @request_id)
  response['postData']
rescue => err
  debug_puts(err)
  nil
end

#finalize_interceptionsObject

Awaits pending interception handlers and then decides how to fulfill the request interception.



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/puppeteer/http_request.rb', line 243

def finalize_interceptions
  @intercept_handlers.each do |handler|
    Puppeteer::AsyncUtils.await(handler.call)
  end
  @intercept_handlers = []
  case intercept_resolution_state.action
  when 'abort'
    abort_impl(@abort_error_reason)
  when 'respond'
    raise "Response is missing for the interception" if @response_for_request.nil?
    respond_impl(**@response_for_request)
  when 'continue'
    continue_impl(@continue_request_overrides)
  end
end

#has_post_data?Boolean

Returns:

  • (Boolean)


138
139
140
# File 'lib/puppeteer/http_request.rb', line 138

def has_post_data?
  @has_post_data
end

#headersObject



133
134
135
# File 'lib/puppeteer/http_request.rb', line 133

def headers
  @headers.dup
end

#inspectObject



151
152
153
154
155
156
157
# File 'lib/puppeteer/http_request.rb', line 151

def inspect
  values = %i[request_id method url].map do |sym|
    value = instance_variable_get(:"@#{sym}")
    "@#{sym}=#{value}"
  end
  "#<Puppeteer::HTTPRequest #{values.join(' ')}>"
end

#intercept_resolution_handled?Boolean

Returns:

  • (Boolean)


227
228
229
# File 'lib/puppeteer/http_request.rb', line 227

def intercept_resolution_handled?
  @interception_handled
end

#intercept_resolution_stateObject



217
218
219
220
221
222
223
224
225
# File 'lib/puppeteer/http_request.rb', line 217

def intercept_resolution_state
  if !@allow_interception
    InterceptResolutionState.disabled
  elsif @interception_handled
    InterceptResolutionState.already_handled
  else
    @intercept_resolution_state.dup
  end
end

Returns:

  • (Boolean)


259
260
261
# File 'lib/puppeteer/http_request.rb', line 259

def navigation_request?
  @is_navigation_request
end

#redirect_chainObject



263
264
265
# File 'lib/puppeteer/http_request.rb', line 263

def redirect_chain
  @redirect_chain.dup
end

#respond(status: nil, headers: nil, content_type: nil, body: nil, priority: nil) ⇒ Object

Mocking response.

Example:

page.on 'request' do |req|
  req.respond(
    status: 404,
    content_type: 'text/plain',
    body: 'Not Found!'
  )
end

Parameters:

  • status (Integer) (defaults to: nil)
  • headers (Hash<String, String>) (defaults to: nil)
  • content_type (String) (defaults to: nil)
  • body (String) (defaults to: nil)


388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
# File 'lib/puppeteer/http_request.rb', line 388

def respond(status: nil, headers: nil, content_type: nil, body: nil, priority: nil)
  assert_interception_allowed
  assert_interception_not_handled
  return unless can_be_intercepted?

  if priority.nil?
    respond_impl(status: status, headers: headers, content_type: content_type, body: body)
    return
  end

  @response_for_request = {
    status: status,
    headers: headers,
    content_type: content_type,
    body: body,
  }.compact

  if @intercept_resolution_state.priority_unspecified? || priority > @intercept_resolution_state.priority
    @intercept_resolution_state = InterceptResolutionState.respond(priority: priority)
    return
  end

  if priority == @intercept_resolution_state.priority
    if @intercept_resolution_state.action == 'abort'
      return
    end
    @intercept_resolution_state = InterceptResolutionState.respond(priority: priority)
  end
end

#response_for_requestObject

interception is allowed to respond (ie, ‘abort()` is not called).



197
198
199
200
# File 'lib/puppeteer/http_request.rb', line 197

def response_for_request
  assert_interception_allowed
  @response_for_request
end

#update_headers(headers) ⇒ Object



127
128
129
130
131
# File 'lib/puppeteer/http_request.rb', line 127

def update_headers(headers)
  headers.each do |key, value|
    @headers[key.downcase] = value
  end
end