puppeteer-ruby 0.0.14 → 0.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.circleci/config.yml +25 -2
- data/.github/workflows/reviewdog.yml +1 -1
- data/.rubocop.yml +49 -3
- data/Dockerfile +9 -0
- data/README.md +3 -1
- data/docker-compose.yml +34 -0
- data/lib/puppeteer.rb +16 -12
- data/lib/puppeteer/browser.rb +36 -9
- data/lib/puppeteer/browser_context.rb +35 -5
- data/lib/puppeteer/browser_runner.rb +1 -1
- data/lib/puppeteer/connection.rb +6 -1
- data/lib/puppeteer/debug_print.rb +2 -2
- data/lib/puppeteer/define_async_method.rb +1 -1
- data/lib/puppeteer/dom_world.rb +78 -52
- data/lib/puppeteer/element_handle.rb +49 -20
- data/lib/puppeteer/env.rb +23 -0
- data/lib/puppeteer/frame.rb +17 -31
- data/lib/puppeteer/js_handle.rb +37 -27
- data/lib/puppeteer/keyboard.rb +3 -2
- data/lib/puppeteer/launcher.rb +11 -1
- data/lib/puppeteer/launcher/base.rb +14 -4
- data/lib/puppeteer/launcher/chrome.rb +1 -1
- data/lib/puppeteer/launcher/firefox.rb +392 -0
- data/lib/puppeteer/mouse.rb +16 -0
- data/lib/puppeteer/network_manager.rb +163 -5
- data/lib/puppeteer/page.rb +195 -125
- data/lib/puppeteer/page/pdf_options.rb +166 -0
- data/lib/puppeteer/remote_object.rb +28 -1
- data/lib/puppeteer/request.rb +330 -0
- data/lib/puppeteer/response.rb +113 -0
- data/lib/puppeteer/version.rb +1 -1
- data/lib/puppeteer/wait_task.rb +1 -1
- data/lib/puppeteer/web_socket.rb +7 -0
- data/puppeteer-ruby.gemspec +2 -1
- metadata +25 -4
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'mime/types'
|
2
|
+
|
3
|
+
class Puppeteer::Page
|
4
|
+
# /**
|
5
|
+
# * @typedef {Object} PDFOptions
|
6
|
+
# * @property {number=} scale
|
7
|
+
# * @property {boolean=} displayHeaderFooter
|
8
|
+
# * @property {string=} headerTemplate
|
9
|
+
# * @property {string=} footerTemplate
|
10
|
+
# * @property {boolean=} printBackground
|
11
|
+
# * @property {boolean=} landscape
|
12
|
+
# * @property {string=} pageRanges
|
13
|
+
# * @property {string=} format
|
14
|
+
# * @property {string|number=} width
|
15
|
+
# * @property {string|number=} height
|
16
|
+
# * @property {boolean=} preferCSSPageSize
|
17
|
+
# * @property {!{top?: string|number, bottom?: string|number, left?: string|number, right?: string|number}=} margin
|
18
|
+
# * @property {string=} path
|
19
|
+
# */
|
20
|
+
class PDFOptions
|
21
|
+
# @params options [Hash]
|
22
|
+
def initialize(options)
|
23
|
+
unless options[:path]
|
24
|
+
# Original puppeteer allows path = nil, however nothing to do without path actually.
|
25
|
+
# Also in most case, users forget to specify path parameter. So let's raise ArgumentError.
|
26
|
+
raise ArgumentError('"path" parameter must be specified.')
|
27
|
+
end
|
28
|
+
|
29
|
+
@scale = options[:scale]
|
30
|
+
@display_header_footer = options[:display_header_footer]
|
31
|
+
@header_template = options[:header_template]
|
32
|
+
@footer_template = options[:footer_template]
|
33
|
+
@print_background = options[:print_background]
|
34
|
+
@landscape = options[:landscape]
|
35
|
+
@page_ranges = options[:page_ranges]
|
36
|
+
@format = options[:format]
|
37
|
+
@width = options[:width]
|
38
|
+
@height = options[:height]
|
39
|
+
@prefer_css_page_size = options[:prefer_css_page_size]
|
40
|
+
@margin = Margin.new(options[:margin] || {})
|
41
|
+
@path = options[:path]
|
42
|
+
end
|
43
|
+
|
44
|
+
attr_reader :path
|
45
|
+
|
46
|
+
class PaperSize
|
47
|
+
def initialize(width:, height:)
|
48
|
+
@width = width
|
49
|
+
@height = height
|
50
|
+
end
|
51
|
+
attr_reader :width, :height
|
52
|
+
end
|
53
|
+
|
54
|
+
PAPER_FORMATS = {
|
55
|
+
letter: PaperSize.new(width: 8.5, height: 11),
|
56
|
+
legal: PaperSize.new(width: 8.5, height: 14),
|
57
|
+
tabloid: PaperSize.new(width: 11, height: 17),
|
58
|
+
ledger: PaperSize.new(width: 17, height: 11),
|
59
|
+
a0: PaperSize.new(width: 33.1, height: 46.8),
|
60
|
+
a1: PaperSize.new(width: 23.4, height: 33.1),
|
61
|
+
a2: PaperSize.new(width: 16.54, height: 23.4),
|
62
|
+
a3: PaperSize.new(width: 11.7, height: 16.54),
|
63
|
+
a4: PaperSize.new(width: 8.27, height: 11.7),
|
64
|
+
a5: PaperSize.new(width: 5.83, height: 8.27),
|
65
|
+
a6: PaperSize.new(width: 4.13, height: 5.83),
|
66
|
+
}
|
67
|
+
|
68
|
+
UNIT_TO_PIXELS = {
|
69
|
+
px: 1,
|
70
|
+
in: 96,
|
71
|
+
cm: 37.8,
|
72
|
+
mm: 3.78,
|
73
|
+
}
|
74
|
+
|
75
|
+
# @param parameter [String|Integer|nil]
|
76
|
+
private def convert_print_parameter_to_inches(parameter)
|
77
|
+
return nil if parameter.nil?
|
78
|
+
|
79
|
+
pixels =
|
80
|
+
if parameter.is_a?(Numeric)
|
81
|
+
parameter.to_i
|
82
|
+
elsif parameter.is_a?(String)
|
83
|
+
unit = parameter[-2..-1].downcase
|
84
|
+
value =
|
85
|
+
if UNIT_TO_PIXELS.has_key?(unit)
|
86
|
+
parameter[0...-2].to_i
|
87
|
+
else
|
88
|
+
unit = 'px'
|
89
|
+
parameter.to_i
|
90
|
+
end
|
91
|
+
|
92
|
+
value * UNIT_TO_PIXELS[unit]
|
93
|
+
else
|
94
|
+
raise ArgumentError.new("page.pdf() Cannot handle parameter type: #{parameter.class}")
|
95
|
+
end
|
96
|
+
|
97
|
+
pixels / 96
|
98
|
+
end
|
99
|
+
|
100
|
+
private def paper_size
|
101
|
+
@paper_size ||= calc_paper_size
|
102
|
+
end
|
103
|
+
|
104
|
+
# @return [PaperSize]
|
105
|
+
private def calc_paper_size
|
106
|
+
if @format
|
107
|
+
PAPER_FORMATS[@format.downcase] or raise ArgumentError.new("Unknown paper format: #{@format}")
|
108
|
+
else
|
109
|
+
PaperSize.new(
|
110
|
+
width: convert_print_parameter_to_inches(@width) || 8.5,
|
111
|
+
height: convert_print_parameter_to_inches(@height) || 11.0,
|
112
|
+
)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
class Margin
|
117
|
+
def initialize(options)
|
118
|
+
@top = options[:top]
|
119
|
+
@bottom = options[:bottom]
|
120
|
+
@left = options[:left]
|
121
|
+
@right = options[:right]
|
122
|
+
end
|
123
|
+
|
124
|
+
def translate(&block)
|
125
|
+
new_margin ={
|
126
|
+
top: block.call(@top),
|
127
|
+
bottom: block.call(@bottom),
|
128
|
+
left: block.call(@left),
|
129
|
+
right: block.call(@right),
|
130
|
+
}
|
131
|
+
Margin.new(new_margin)
|
132
|
+
end
|
133
|
+
attr_reader :top, :bottom, :left, :right
|
134
|
+
end
|
135
|
+
|
136
|
+
private def margin
|
137
|
+
@__margin ||= calc_margin
|
138
|
+
end
|
139
|
+
|
140
|
+
private def calc_margin
|
141
|
+
@margin.translate do |value|
|
142
|
+
convert_print_parameter_to_inches(value) || 0
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def page_print_args
|
147
|
+
{
|
148
|
+
transferMode: 'ReturnAsStream',
|
149
|
+
landscape: @landscape || false,
|
150
|
+
displayHeaderFooter: @display_header_footer || false,
|
151
|
+
headerTemplate: @header_template || '',
|
152
|
+
footerTemplate: @footer_template || '',
|
153
|
+
printBackground: @print_background || false,
|
154
|
+
scale: @scale || 1,
|
155
|
+
paperWidth: paper_size.width,
|
156
|
+
paperHeight: paper_size.height,
|
157
|
+
marginTop: margin.top,
|
158
|
+
marginBottom: margin.bottom,
|
159
|
+
marginLeft: margin.left,
|
160
|
+
marginRight: margin.right,
|
161
|
+
pageRanges: @page_ranges || '',
|
162
|
+
preferCSSPageSize: @prefer_css_page_size || false,
|
163
|
+
}
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
@@ -6,6 +6,7 @@ class Puppeteer::RemoteObject
|
|
6
6
|
# @param payload [Hash]
|
7
7
|
def initialize(payload)
|
8
8
|
@object_id = payload['objectId']
|
9
|
+
@type = payload['type']
|
9
10
|
@sub_type = payload['subtype']
|
10
11
|
@unserializable_value = payload['unserializableValue']
|
11
12
|
@value = payload['value']
|
@@ -42,6 +43,21 @@ class Puppeteer::RemoteObject
|
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
46
|
+
# @return [String]
|
47
|
+
def type_str
|
48
|
+
# used in JSHandle#to_s
|
49
|
+
# original logic:
|
50
|
+
# if (this._remoteObject.objectId) {
|
51
|
+
# const type = this._remoteObject.subtype || this._remoteObject.type;
|
52
|
+
# return 'JSHandle@' + type;
|
53
|
+
# }
|
54
|
+
if @object_id
|
55
|
+
@sub_type || @type
|
56
|
+
else
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
45
61
|
# used in JSHandle#properties
|
46
62
|
def properties(client)
|
47
63
|
# original logic:
|
@@ -64,7 +80,18 @@ class Puppeteer::RemoteObject
|
|
64
80
|
|
65
81
|
# used in ElementHandle#_box_model
|
66
82
|
def box_model(client)
|
67
|
-
client.send_message('DOM.getBoxModel', objectId: @object_id)
|
83
|
+
result = client.send_message('DOM.getBoxModel', objectId: @object_id)
|
84
|
+
|
85
|
+
# Firefox returns width/height = 0, content/padding/border/margin = [nil, nil, nil, nil, nil, nil, nil, nil]
|
86
|
+
# while Chrome throws Error(Could not compute box model)
|
87
|
+
model = result['model']
|
88
|
+
if model['width'] == 0 && model['height'] == 0 &&
|
89
|
+
%w(content padding border margin).all? { |key| model[key].all?(&:nil?) }
|
90
|
+
|
91
|
+
debug_puts('Could not compute box model in Firefox.')
|
92
|
+
return nil
|
93
|
+
end
|
94
|
+
result
|
68
95
|
rescue => err
|
69
96
|
debug_puts(err)
|
70
97
|
nil
|
@@ -0,0 +1,330 @@
|
|
1
|
+
class Puppeteer::Request
|
2
|
+
include Puppeteer::DebugPrint
|
3
|
+
include Puppeteer::IfPresent
|
4
|
+
|
5
|
+
# defines some methods used only in NetworkManager, Response
|
6
|
+
class InternalAccessor
|
7
|
+
def initialize(request)
|
8
|
+
@request = request
|
9
|
+
end
|
10
|
+
|
11
|
+
def request_id
|
12
|
+
@request.instance_variable_get(:@request_id)
|
13
|
+
end
|
14
|
+
|
15
|
+
def interception_id
|
16
|
+
@request.instance_variable_get(:@interception_id)
|
17
|
+
end
|
18
|
+
|
19
|
+
# @param response [Puppeteer::Response]
|
20
|
+
def response=(response)
|
21
|
+
@request.instance_variable_set(:@response, response)
|
22
|
+
end
|
23
|
+
|
24
|
+
def redirect_chain
|
25
|
+
@request.instance_variable_get(:@redirect_chain)
|
26
|
+
end
|
27
|
+
|
28
|
+
def failure_text=(failure_text)
|
29
|
+
@request.instance_variable_set(:@failure_text, failure_text)
|
30
|
+
end
|
31
|
+
|
32
|
+
def from_memory_cache=(from_memory_cache)
|
33
|
+
@request.instance_variable_set(:@from_memory_cache, from_memory_cache)
|
34
|
+
end
|
35
|
+
|
36
|
+
def from_memory_cache?
|
37
|
+
@request.instance_variable_get(:@from_memory_cache)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# @param client [Puppeteer::CDPSession]
|
42
|
+
# @param frame [Puppeteer::Frame]
|
43
|
+
# @param interception_id [string|nil]
|
44
|
+
# @param allow_interception [boolean]
|
45
|
+
# @param event [Hash]
|
46
|
+
# @param redirect_chain Array<Request>
|
47
|
+
def initialize(client, frame, interception_id, allow_interception, event, redirect_chain)
|
48
|
+
@client = client
|
49
|
+
@request_id = event['requestId']
|
50
|
+
@is_navigation_request = event['requestId'] == event['loaderId'] && event['type'] == 'Document'
|
51
|
+
@interception_id = interception_id
|
52
|
+
@allow_interception = allow_interception
|
53
|
+
@url = event['request']['url']
|
54
|
+
@resource_type = event['type'].downcase
|
55
|
+
@method = event['request']['method']
|
56
|
+
@post_data = event['request']['postData']
|
57
|
+
@frame = frame
|
58
|
+
@redirect_chain = redirect_chain
|
59
|
+
@headers = {}
|
60
|
+
event['request']['headers'].each do |key, value|
|
61
|
+
@headers[key.downcase] = value
|
62
|
+
end
|
63
|
+
@from_memory_cache = false
|
64
|
+
|
65
|
+
@internal = InternalAccessor.new(self)
|
66
|
+
end
|
67
|
+
|
68
|
+
attr_reader :internal
|
69
|
+
attr_reader :url, :resource_type, :method, :post_data, :headers, :response, :frame
|
70
|
+
|
71
|
+
def navigation_request?
|
72
|
+
@is_navigation_request
|
73
|
+
end
|
74
|
+
|
75
|
+
def redirect_chain
|
76
|
+
@redirect_chain.dup
|
77
|
+
end
|
78
|
+
|
79
|
+
def failure
|
80
|
+
if_present(@failure_text) do |failure_text|
|
81
|
+
{ errorText: @failure_text }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
private def headers_to_array(headers)
|
86
|
+
return nil unless headers
|
87
|
+
|
88
|
+
headers.map do |key, value|
|
89
|
+
{ name: key, value: value.to_s }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class InterceptionNotEnabledError < StandardError
|
94
|
+
def initialize
|
95
|
+
super('Request Interception is not enabled!')
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class AlreadyHandledError < StandardError
|
100
|
+
def initialize
|
101
|
+
super('Request is already handled!')
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# proceed request on request interception.
|
106
|
+
#
|
107
|
+
# Example:
|
108
|
+
#
|
109
|
+
# page.on 'request' do |req|
|
110
|
+
# # Override headers
|
111
|
+
# headers = req.headers.merge(
|
112
|
+
# foo: 'bar', # set "foo" header
|
113
|
+
# origin: nil, # remove "origin" header
|
114
|
+
# )
|
115
|
+
# req.continue(headers: headers)
|
116
|
+
# end
|
117
|
+
#
|
118
|
+
# @param error_code [String|Symbol]
|
119
|
+
def continue(url: nil, method: nil, post_data: nil, headers: nil)
|
120
|
+
# Request interception is not supported for data: urls.
|
121
|
+
return if @url.start_with?('data:')
|
122
|
+
|
123
|
+
unless @allow_interception
|
124
|
+
raise InterceptionNotEnabledError.new
|
125
|
+
end
|
126
|
+
if @interception_handled
|
127
|
+
raise AlreadyHandledError.new
|
128
|
+
end
|
129
|
+
@interception_handled = true
|
130
|
+
|
131
|
+
overrides = {
|
132
|
+
url: url,
|
133
|
+
method: method,
|
134
|
+
post_data: post_data,
|
135
|
+
headers: headers_to_array(headers),
|
136
|
+
}.compact
|
137
|
+
begin
|
138
|
+
@client.send_message('Fetch.continueRequest',
|
139
|
+
requestId: @interception_id,
|
140
|
+
**overrides,
|
141
|
+
)
|
142
|
+
rescue => err
|
143
|
+
# In certain cases, protocol will return error if the request was already canceled
|
144
|
+
# or the page was closed. We should tolerate these errors.
|
145
|
+
debug_puts(err)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Mocking response.
|
150
|
+
#
|
151
|
+
# Example:
|
152
|
+
#
|
153
|
+
# page.on 'request' do |req|
|
154
|
+
# req.respond(
|
155
|
+
# status: 404,
|
156
|
+
# content_type: 'text/plain',
|
157
|
+
# body: 'Not Found!'
|
158
|
+
# )
|
159
|
+
# end
|
160
|
+
#
|
161
|
+
# @param status [Integer]
|
162
|
+
# @param headers [Hash<String, String>]
|
163
|
+
# @param content_type [String]
|
164
|
+
# @param body [String]
|
165
|
+
def respond(status: nil, headers: nil, content_type: nil, body: nil)
|
166
|
+
# Mocking responses for dataURL requests is not currently supported.
|
167
|
+
return if @url.start_with?('data:')
|
168
|
+
|
169
|
+
unless @allow_interception
|
170
|
+
raise InterceptionNotEnabledError.new
|
171
|
+
end
|
172
|
+
if @interception_handled
|
173
|
+
raise AlreadyHandledError.new
|
174
|
+
end
|
175
|
+
@interception_handled = true
|
176
|
+
|
177
|
+
mock_response_headers = {}
|
178
|
+
headers&.each do |key, value|
|
179
|
+
mock_response_headers[key.downcase] = value
|
180
|
+
end
|
181
|
+
if content_type
|
182
|
+
mock_response_headers['content-type'] = content_type
|
183
|
+
end
|
184
|
+
if body
|
185
|
+
mock_response_headers['content-length'] = body.length
|
186
|
+
end
|
187
|
+
|
188
|
+
mock_response = {
|
189
|
+
responseCode: status || 200,
|
190
|
+
responsePhrase: STATUS_TEXTS[(status || 200).to_s],
|
191
|
+
responseHeaders: headers_to_array(mock_response_headers),
|
192
|
+
body: if_present(body) { |mock_body| Base64.strict_encode64(mock_body) },
|
193
|
+
}.compact
|
194
|
+
begin
|
195
|
+
@client.send_message('Fetch.fulfillRequest',
|
196
|
+
requestId: @interception_id,
|
197
|
+
**mock_response,
|
198
|
+
)
|
199
|
+
rescue => err
|
200
|
+
# In certain cases, protocol will return error if the request was already canceled
|
201
|
+
# or the page was closed. We should tolerate these errors.
|
202
|
+
debug_puts(err)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# abort request on request interception.
|
207
|
+
#
|
208
|
+
# Example:
|
209
|
+
#
|
210
|
+
# page.on 'request' do |req|
|
211
|
+
# if req.url.include?("porn")
|
212
|
+
# req.abort
|
213
|
+
# else
|
214
|
+
# req.continue
|
215
|
+
# end
|
216
|
+
# end
|
217
|
+
#
|
218
|
+
# @param error_code [String|Symbol]
|
219
|
+
def abort(error_code: :failed)
|
220
|
+
# Request interception is not supported for data: urls.
|
221
|
+
return if @url.start_with?('data:')
|
222
|
+
|
223
|
+
error_reason = ERROR_REASONS[error_code.to_s]
|
224
|
+
unless error_reason
|
225
|
+
raise ArgumentError.new("Unknown error code: #{error_code}")
|
226
|
+
end
|
227
|
+
unless @allow_interception
|
228
|
+
raise InterceptionNotEnabledError.new
|
229
|
+
end
|
230
|
+
if @interception_handled
|
231
|
+
raise AlreadyHandledError.new
|
232
|
+
end
|
233
|
+
@interception_handled = true
|
234
|
+
|
235
|
+
begin
|
236
|
+
@client.send_message('Fetch.failRequest',
|
237
|
+
requestId: @interception_id,
|
238
|
+
errorReason: error_reason,
|
239
|
+
)
|
240
|
+
rescue => err
|
241
|
+
# In certain cases, protocol will return error if the request was already canceled
|
242
|
+
# or the page was closed. We should tolerate these errors.
|
243
|
+
debug_puts(err)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
ERROR_REASONS = {
|
248
|
+
'aborted' => 'Aborted',
|
249
|
+
'accessdenied' => 'AccessDenied',
|
250
|
+
'addressunreachable' => 'AddressUnreachable',
|
251
|
+
'blockedbyclient' => 'BlockedByClient',
|
252
|
+
'blockedbyresponse' => 'BlockedByResponse',
|
253
|
+
'connectionaborted' => 'ConnectionAborted',
|
254
|
+
'connectionclosed' => 'ConnectionClosed',
|
255
|
+
'connectionfailed' => 'ConnectionFailed',
|
256
|
+
'connectionrefused' => 'ConnectionRefused',
|
257
|
+
'connectionreset' => 'ConnectionReset',
|
258
|
+
'internetdisconnected' => 'InternetDisconnected',
|
259
|
+
'namenotresolved' => 'NameNotResolved',
|
260
|
+
'timedout' => 'TimedOut',
|
261
|
+
'failed' => 'Failed',
|
262
|
+
}.freeze
|
263
|
+
|
264
|
+
# List taken from https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml with extra 306 and 418 codes.
|
265
|
+
STATUS_TEXTS = {
|
266
|
+
'100' => 'Continue',
|
267
|
+
'101' => 'Switching Protocols',
|
268
|
+
'102' => 'Processing',
|
269
|
+
'103' => 'Early Hints',
|
270
|
+
'200' => 'OK',
|
271
|
+
'201' => 'Created',
|
272
|
+
'202' => 'Accepted',
|
273
|
+
'203' => 'Non-Authoritative Information',
|
274
|
+
'204' => 'No Content',
|
275
|
+
'205' => 'Reset Content',
|
276
|
+
'206' => 'Partial Content',
|
277
|
+
'207' => 'Multi-Status',
|
278
|
+
'208' => 'Already Reported',
|
279
|
+
'226' => 'IM Used',
|
280
|
+
'300' => 'Multiple Choices',
|
281
|
+
'301' => 'Moved Permanently',
|
282
|
+
'302' => 'Found',
|
283
|
+
'303' => 'See Other',
|
284
|
+
'304' => 'Not Modified',
|
285
|
+
'305' => 'Use Proxy',
|
286
|
+
'306' => 'Switch Proxy',
|
287
|
+
'307' => 'Temporary Redirect',
|
288
|
+
'308' => 'Permanent Redirect',
|
289
|
+
'400' => 'Bad Request',
|
290
|
+
'401' => 'Unauthorized',
|
291
|
+
'402' => 'Payment Required',
|
292
|
+
'403' => 'Forbidden',
|
293
|
+
'404' => 'Not Found',
|
294
|
+
'405' => 'Method Not Allowed',
|
295
|
+
'406' => 'Not Acceptable',
|
296
|
+
'407' => 'Proxy Authentication Required',
|
297
|
+
'408' => 'Request Timeout',
|
298
|
+
'409' => 'Conflict',
|
299
|
+
'410' => 'Gone',
|
300
|
+
'411' => 'Length Required',
|
301
|
+
'412' => 'Precondition Failed',
|
302
|
+
'413' => 'Payload Too Large',
|
303
|
+
'414' => 'URI Too Long',
|
304
|
+
'415' => 'Unsupported Media Type',
|
305
|
+
'416' => 'Range Not Satisfiable',
|
306
|
+
'417' => 'Expectation Failed',
|
307
|
+
'418' => 'I\'m a teapot',
|
308
|
+
'421' => 'Misdirected Request',
|
309
|
+
'422' => 'Unprocessable Entity',
|
310
|
+
'423' => 'Locked',
|
311
|
+
'424' => 'Failed Dependency',
|
312
|
+
'425' => 'Too Early',
|
313
|
+
'426' => 'Upgrade Required',
|
314
|
+
'428' => 'Precondition Required',
|
315
|
+
'429' => 'Too Many Requests',
|
316
|
+
'431' => 'Request Header Fields Too Large',
|
317
|
+
'451' => 'Unavailable For Legal Reasons',
|
318
|
+
'500' => 'Internal Server Error',
|
319
|
+
'501' => 'Not Implemented',
|
320
|
+
'502' => 'Bad Gateway',
|
321
|
+
'503' => 'Service Unavailable',
|
322
|
+
'504' => 'Gateway Timeout',
|
323
|
+
'505' => 'HTTP Version Not Supported',
|
324
|
+
'506' => 'Variant Also Negotiates',
|
325
|
+
'507' => 'Insufficient Storage',
|
326
|
+
'508' => 'Loop Detected',
|
327
|
+
'510' => 'Not Extended',
|
328
|
+
'511' => 'Network Authentication Required',
|
329
|
+
}.freeze
|
330
|
+
end
|