Class: Puppeteer::Bidi::HTTPRequest

Inherits:
Object
  • Object
show all
Defined in:
lib/puppeteer/bidi/http_request.rb,
sig/puppeteer/bidi/http_request.rbs

Overview

HTTPRequest represents a request initiated by a page.

Defined Under Namespace

Modules: InterceptResolutionAction

Constant Summary collapse

DEFAULT_INTERCEPT_RESOLUTION_PRIORITY =

Returns:

  • (::Integer)
0
STATUS_TEXTS =

Returns:

  • (Object)
{
  "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
ERROR_REASONS =

Returns:

  • (Object)
{
  "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
REQUESTS =

Returns:

  • (Object)
begin
  ObjectSpace::WeakMap.new
rescue NameError
  {}
end

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(core_request, frame, interception_enabled, redirect) ⇒ HTTPRequest

Returns a new instance of HTTPRequest.

Parameters:



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/puppeteer/bidi/http_request.rb', line 134

def initialize(core_request, frame, interception_enabled, redirect)
  @request = core_request
  @frame = frame
  @redirect_chain = redirect ? redirect.send(:redirect_chain_internal) : []
  @response = nil
  @authentication_handled = false
  @from_memory_cache = false
  @id = core_request.id

  @interception = {
    enabled: interception_enabled,
    handled: false,
    handlers: [],
    resolution_state: { action: InterceptResolutionAction::NONE },
    request_overrides: {},
    response: nil,
    abort_reason: nil,
  }

  REQUESTS[@request] = self
end

Instance Attribute Details

#idObject (readonly)

Returns the value of attribute id.

Returns:

  • (Object)


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

def id
  @id
end

Class Method Details

.bidi_headers_from_hash(raw_headers) ⇒ Object

Parameters:

  • raw_headers (Object)

Returns:

  • (Object)


544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
# File 'lib/puppeteer/bidi/http_request.rb', line 544

def self.bidi_headers_from_hash(raw_headers)
  headers = []
  (raw_headers || {}).each do |name, value|
    next if value.nil?

    values = value.is_a?(Array) ? value : [value]
    values.each do |header_value|
      headers << {
        "name" => name.to_s.downcase,
        "value" => {
          "type" => "string",
          "value" => header_value.to_s,
        },
      }
    end
  end
  headers
end

.for_core_request(core_request) ⇒ HTTPRequest?

Parameters:

Returns:



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

def self.for_core_request(core_request)
  REQUESTS[core_request]
end

.from(core_request, frame, interception_enabled, redirect: nil) ⇒ HTTPRequest

Parameters:

Returns:



115
116
117
118
119
# File 'lib/puppeteer/bidi/http_request.rb', line 115

def self.from(core_request, frame, interception_enabled, redirect: nil)
  request = new(core_request, frame, interception_enabled, redirect)
  request.send(:initialize_request)
  request
end

.get_response(body) ⇒ Hash[Symbol, untyped]

Parameters:

  • body (String)

Returns:

  • (Hash[Symbol, untyped])


378
379
380
381
382
383
384
# File 'lib/puppeteer/bidi/http_request.rb', line 378

def self.get_response(body)
  bytes = body.is_a?(String) ? body.dup.force_encoding("BINARY") : body
  {
    content_length: bytes.bytesize,
    base64: Base64.strict_encode64(bytes),
  }
end

.handle_interception_error(error) ⇒ Object

Parameters:

  • error (Object)

Returns:

  • (Object)


563
564
565
566
567
568
569
570
571
572
573
574
# File 'lib/puppeteer/bidi/http_request.rb', line 563

def self.handle_interception_error(error)
  message = error.message.to_s
  if message.include?("Invalid header") ||
     message.include?("Unsafe header") ||
     message.include?('Expected "header"') ||
     message.include?("invalid argument")
    raise error
  end

  warn(error.full_message) if ENV["DEBUG_BIDI_COMMAND"]
  nil
end

Instance Method Details

#_abort(_error_reason) ⇒ Object

Parameters:

  • _error_reason (Object)

Returns:

  • (Object)


456
457
458
459
460
461
462
463
464
# File 'lib/puppeteer/bidi/http_request.rb', line 456

def _abort(_error_reason)
  @interception[:handled] = true
  begin
    @request.fail_request.wait
  rescue => error
    @interception[:handled] = false
    raise error
  end
end

#_continue(overrides) ⇒ Object

Parameters:

  • overrides (Object)

Returns:

  • (Object)


439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
# File 'lib/puppeteer/bidi/http_request.rb', line 439

def _continue(overrides)
  headers = self.class.bidi_headers_from_hash(value_for_key(overrides, :headers, "headers"))
  @interception[:handled] = true

  begin
    @request.continue_request(
      url: value_for_key(overrides, :url, "url"),
      method: value_for_key(overrides, :method, "method"),
      body: body_override(value_for_key(overrides, :postData, "postData")),
      headers: headers.empty? ? nil : headers,
    ).wait
  rescue => error
    @interception[:handled] = false
    self.class.handle_interception_error(error)
  end
end

#_respond(response) ⇒ Object

Parameters:

  • response (Object)

Returns:

  • (Object)


466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
# File 'lib/puppeteer/bidi/http_request.rb', line 466

def _respond(response)
  @interception[:handled] = true

  parsed_body = nil
  body = value_for_key(response, :body, "body")
  parsed_body = self.class.get_response(body) if body

  headers = self.class.bidi_headers_from_hash(value_for_key(response, :headers, "headers"))
  has_content_length = headers.any? { |header| header["name"] == "content-length" }

  content_type = value_for_key(response, :contentType, "contentType")
  if content_type
    headers << {
      "name" => "content-type",
      "value" => { "type" => "string", "value" => content_type.to_s },
    }
  end

  if parsed_body && !has_content_length
    headers << {
      "name" => "content-length",
      "value" => { "type" => "string", "value" => parsed_body[:content_length].to_s },
    }
  end

  status = value_for_key(response, :status, "status") || 200

  begin
    @request.provide_response(
      status_code: status,
      reason_phrase: STATUS_TEXTS[status.to_s],
      headers: headers.empty? ? nil : headers,
      body: parsed_body ? { type: "base64", value: parsed_body[:base64] } : nil,
    ).wait
  rescue => error
    @interception[:handled] = false
    raise error
  end
end

#abort(error_code = "failed", priority = nil) ⇒ void

This method returns an undefined value.

Parameters:

  • error_code (String) (defaults to: "failed")
  • priority (Integer, nil) (defaults to: nil)


354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
# File 'lib/puppeteer/bidi/http_request.rb', line 354

def abort(error_code = "failed", priority = nil)
  verify_interception
  return unless can_be_intercepted?

  error_reason = ERROR_REASONS[error_code]
  raise Error, "Unknown error code: #{error_code}" unless error_reason

  if priority.nil?
    return _abort(error_reason)
  end

  @interception[:abort_reason] = error_reason
  if @interception[:resolution_state][:priority].nil? || priority >= @interception[:resolution_state][:priority]
    @interception[:resolution_state] = { action: InterceptResolutionAction::ABORT, priority: priority }
  end
end

#abort_error_reasonString?

Returns:

  • (String, nil)


274
275
276
# File 'lib/puppeteer/bidi/http_request.rb', line 274

def abort_error_reason
  @interception[:abort_reason]
end

#body_override(post_data) ⇒ Object

Parameters:

  • post_data (Object)

Returns:

  • (Object)


523
524
525
526
527
528
529
530
# File 'lib/puppeteer/bidi/http_request.rb', line 523

def body_override(post_data)
  return nil if post_data.nil?

  {
    type: "base64",
    value: Base64.strict_encode64(post_data.to_s.b),
  }
end

#can_be_intercepted?Boolean

Returns:

  • (Boolean)


435
436
437
# File 'lib/puppeteer/bidi/http_request.rb', line 435

def can_be_intercepted?
  @request.blocked?
end

#continue(overrides = {}, priority = nil) ⇒ void

This method returns an undefined value.

Parameters:

  • overrides (Hash[Symbol | String, untyped]) (defaults to: {})
  • priority (Integer, nil) (defaults to: nil)


306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
# File 'lib/puppeteer/bidi/http_request.rb', line 306

def continue(overrides = {}, priority = nil)
  verify_interception
  return unless can_be_intercepted?

  if priority.nil?
    return _continue(overrides)
  end

  @interception[:request_overrides] = overrides
  if @interception[:resolution_state][:priority].nil? || priority > @interception[:resolution_state][:priority]
    @interception[:resolution_state] = { action: InterceptResolutionAction::CONTINUE, priority: priority }
    return
  end
  if priority == @interception[:resolution_state][:priority]
    return if [InterceptResolutionAction::ABORT, InterceptResolutionAction::RESPOND].include?(
      @interception[:resolution_state][:action],
    )

    @interception[:resolution_state][:action] = InterceptResolutionAction::CONTINUE
  end
end

#continue_request_overridesHash[Symbol | String, untyped]

Returns:

  • (Hash[Symbol | String, untyped])


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

def continue_request_overrides
  @interception[:request_overrides]
end

#enqueue_intercept_action(&block) ⇒ void

This method returns an undefined value.



280
281
282
# File 'lib/puppeteer/bidi/http_request.rb', line 280

def enqueue_intercept_action(&block)
  @interception[:handlers] << block
end

#failureHash[String, String]?

Returns:

  • (Hash[String, String], nil)


221
222
223
224
225
# File 'lib/puppeteer/bidi/http_request.rb', line 221

def failure
  return nil if @request.error.nil?

  { "errorText" => @request.error }
end

#fetch_post_dataString?

Returns:

  • (String, nil)


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

def fetch_post_data
  @request.fetch_post_data.wait
end

#finalize_interceptionsvoid

This method returns an undefined value.



285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# File 'lib/puppeteer/bidi/http_request.rb', line 285

def finalize_interceptions
  @interception[:handlers].each do |handler|
    AsyncUtils.await(handler.call)
  end
  @interception[:handlers] = []

  case intercept_resolution_state[:action]
  when InterceptResolutionAction::ABORT
    _abort(@interception[:abort_reason])
  when InterceptResolutionAction::RESPOND
    raise "Response is missing for the interception" if @interception[:response].nil?

    _respond(@interception[:response])
  when InterceptResolutionAction::CONTINUE
    _continue(@interception[:request_overrides])
  end
end

#frameFrame

Returns:



201
202
203
# File 'lib/puppeteer/bidi/http_request.rb', line 201

def frame
  @frame
end

#get_response_contentString

Returns:

  • (String)


372
373
374
# File 'lib/puppeteer/bidi/http_request.rb', line 372

def get_response_content
  @request.response_content.wait
end

#handle_authenticationObject

Returns:

  • (Object)


506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
# File 'lib/puppeteer/bidi/http_request.rb', line 506

def handle_authentication
  credentials = @frame.page.credentials
  if credentials && !@authentication_handled
    @authentication_handled = true
    @request.continue_with_auth(
      action: "provideCredentials",
      credentials: {
        "type" => "password",
        "username" => credentials[:username],
        "password" => credentials[:password],
      },
    ).wait
  else
    @request.continue_with_auth(action: "cancel").wait
  end
end

#has_post_data?Boolean

Returns:

  • (Boolean)


186
187
188
# File 'lib/puppeteer/bidi/http_request.rb', line 186

def has_post_data?
  @request.has_post_data?
end

#headersHash[String, String]

Returns:

  • (Hash[String, String])


167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/puppeteer/bidi/http_request.rb', line 167

def headers
  headers = {}
  @request.headers.each do |header|
    name = header["name"].to_s.downcase
    value = header["value"]
    next unless value.is_a?(Hash)
    next unless value["type"] == "string"

    headers[name] = value["value"]
  end
  headers.dup
end

#initialize_requestObject

Returns:

  • (Object)


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
417
418
419
420
421
422
423
424
# File 'lib/puppeteer/bidi/http_request.rb', line 388

def initialize_request
  @request.on(:redirect) do |redirect_request|
    http_request = HTTPRequest.from(
      redirect_request,
      @frame,
      @interception[:enabled],
      redirect: self,
    )
    @redirect_chain << self

    redirect_request.once(:success) do
      @frame.page.emit(:requestfinished, http_request)
    end

    redirect_request.once(:error) do
      @frame.page.emit(:requestfailed, http_request)
    end

    Async do
      http_request.finalize_interceptions
    end
  end

  @request.once(:response) do |data|
    @response = HTTPResponse.from(data, self)
  end

  @request.once(:success) do |data|
    @response = HTTPResponse.from(data, self)
  end

  @request.on(:authenticate) do
    handle_authentication
  end

  @frame.page.emit(:request, self)
end

#initiatorHash[String, untyped]?

Returns:

  • (Hash[String, untyped], nil)


228
229
230
231
232
233
234
235
236
237
238
# File 'lib/puppeteer/bidi/http_request.rb', line 228

def initiator
  initiator = @request.initiator
  return nil unless initiator

  normalized = {}
  initiator.each do |key, value|
    normalized[key.to_s] = value
  end
  normalized["type"] ||= "other"
  normalized
end

#intercept_resolution_handled?Boolean

Returns:

  • (Boolean)


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

def intercept_resolution_handled?
  @interception[:handled]
end

#intercept_resolution_stateHash[Symbol, untyped]

Returns:

  • (Hash[Symbol, untyped])


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

def intercept_resolution_state
  return { action: InterceptResolutionAction::DISABLED } unless @request.blocked?

  if !@interception[:enabled]
    return { action: InterceptResolutionAction::DISABLED }
  end
  if @interception[:handled]
    return { action: InterceptResolutionAction::ALREADY_HANDLED }
  end
  @interception[:resolution_state].dup
end

#methodString

Returns:

  • (String)


162
163
164
# File 'lib/puppeteer/bidi/http_request.rb', line 162

def method
  @request.method
end

Returns:

  • (Boolean)


206
207
208
# File 'lib/puppeteer/bidi/http_request.rb', line 206

def navigation_request?
  !@request.navigation.nil?
end

#post_dataString?

Returns:

  • (String, nil)


181
182
183
# File 'lib/puppeteer/bidi/http_request.rb', line 181

def post_data
  @request.post_data
end

#redirect_chainArray[HTTPRequest]

Returns:



211
212
213
# File 'lib/puppeteer/bidi/http_request.rb', line 211

def redirect_chain
  @redirect_chain.dup
end

#redirect_chain_internalObject

Returns:

  • (Object)


426
427
428
# File 'lib/puppeteer/bidi/http_request.rb', line 426

def redirect_chain_internal
  @redirect_chain
end

#resource_typeString

Returns:

  • (String)


196
197
198
# File 'lib/puppeteer/bidi/http_request.rb', line 196

def resource_type
  (@request.resource_type || "other").downcase
end

#respond(response = {}, priority = nil) ⇒ void

This method returns an undefined value.

Parameters:

  • response (Hash[Symbol | String, untyped]) (defaults to: {})
  • priority (Integer, nil) (defaults to: nil)


331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/puppeteer/bidi/http_request.rb', line 331

def respond(response = {}, priority = nil)
  verify_interception
  return unless can_be_intercepted?

  if priority.nil?
    return _respond(response)
  end

  @interception[:response] = response
  if @interception[:resolution_state][:priority].nil? || priority > @interception[:resolution_state][:priority]
    @interception[:resolution_state] = { action: InterceptResolutionAction::RESPOND, priority: priority }
    return
  end
  if priority == @interception[:resolution_state][:priority]
    return if @interception[:resolution_state][:action] == InterceptResolutionAction::ABORT

    @interception[:resolution_state][:action] = InterceptResolutionAction::RESPOND
  end
end

#responseHTTPResponse?

Returns:



216
217
218
# File 'lib/puppeteer/bidi/http_request.rb', line 216

def response
  @response
end

#response_for_requestHash[Symbol | String, untyped]?

Returns:

  • (Hash[Symbol | String, untyped], nil)


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

def response_for_request
  @interception[:response]
end

#timingHash[String, untyped]

Returns:

  • (Hash[String, untyped])


241
242
243
# File 'lib/puppeteer/bidi/http_request.rb', line 241

def timing
  @request.timing
end

#urlString

Returns:

  • (String)


157
158
159
# File 'lib/puppeteer/bidi/http_request.rb', line 157

def url
  @request.url
end

#value_for_key(hash, symbol_key, string_key) ⇒ Object

Parameters:

  • hash (Object)
  • symbol_key (Object)
  • string_key (Object)

Returns:

  • (Object)


532
533
534
535
536
537
538
539
540
541
542
# File 'lib/puppeteer/bidi/http_request.rb', line 532

def value_for_key(hash, symbol_key, string_key)
  return nil unless hash

  if hash.key?(symbol_key)
    hash[symbol_key]
  elsif hash.key?(string_key)
    hash[string_key]
  else
    nil
  end
end

#verify_interceptionObject

Returns:

  • (Object)

Raises:



430
431
432
433
# File 'lib/puppeteer/bidi/http_request.rb', line 430

def verify_interception
  raise Error, "Request Interception is not enabled!" unless @interception[:enabled]
  raise Error, "Request is already handled!" if @interception[:handled]
end