akephalos2 2.0.7-java
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.
- data/MIT_LICENSE +20 -0
- data/README.md +330 -0
- data/bin/akephalos +102 -0
- data/lib/akephalos/capybara.rb +347 -0
- data/lib/akephalos/client/cookies.rb +73 -0
- data/lib/akephalos/client/filter.rb +120 -0
- data/lib/akephalos/client.rb +192 -0
- data/lib/akephalos/configuration.rb +49 -0
- data/lib/akephalos/console.rb +32 -0
- data/lib/akephalos/cucumber.rb +6 -0
- data/lib/akephalos/htmlunit/ext/confirm_handler.rb +18 -0
- data/lib/akephalos/htmlunit/ext/http_method.rb +30 -0
- data/lib/akephalos/htmlunit.rb +31 -0
- data/lib/akephalos/node.rb +192 -0
- data/lib/akephalos/page.rb +118 -0
- data/lib/akephalos/remote_client.rb +93 -0
- data/lib/akephalos/server.rb +79 -0
- data/lib/akephalos/version.rb +3 -0
- data/lib/akephalos.rb +19 -0
- data/vendor/html-unit/apache-mime4j-0.6.jar +0 -0
- data/vendor/html-unit/commons-codec-1.4.jar +0 -0
- data/vendor/html-unit/commons-collections-3.2.1.jar +0 -0
- data/vendor/html-unit/commons-io-2.0.1.jar +0 -0
- data/vendor/html-unit/commons-lang3-3.0.1.jar +0 -0
- data/vendor/html-unit/commons-logging-1.1.1.jar +0 -0
- data/vendor/html-unit/cssparser-0.9.6-20110829.205617-3.jar +0 -0
- data/vendor/html-unit/htmlunit-2.10-SNAPSHOT.jar +0 -0
- data/vendor/html-unit/htmlunit-core-js-2.9.jar +0 -0
- data/vendor/html-unit/httpclient-4.1.2.jar +0 -0
- data/vendor/html-unit/httpcore-4.1.2.jar +0 -0
- data/vendor/html-unit/httpmime-4.1.2.jar +0 -0
- data/vendor/html-unit/nekohtml-1.9.15.jar +0 -0
- data/vendor/html-unit/sac-1.3.jar +0 -0
- data/vendor/html-unit/serializer-2.7.1.jar +0 -0
- data/vendor/html-unit/xalan-2.7.1.jar +0 -0
- data/vendor/html-unit/xercesImpl-2.9.1.jar +0 -0
- data/vendor/html-unit/xml-apis-1.3.04.jar +0 -0
- metadata +127 -0
@@ -0,0 +1,347 @@
|
|
1
|
+
# Driver class exposed to Capybara. It implements Capybara's full driver API,
|
2
|
+
# and is the entry point for interaction between the test suites and HtmlUnit.
|
3
|
+
#
|
4
|
+
# This class and +Capybara::Driver::Akephalos::Node+ are written to run on both
|
5
|
+
# MRI and JRuby, and is agnostic whether the Akephalos::Client instance is used
|
6
|
+
# directly or over DRb.
|
7
|
+
class Capybara::Driver::Akephalos < Capybara::Driver::Base
|
8
|
+
|
9
|
+
# Akephalos-specific implementation for Capybara's Driver::Node class.
|
10
|
+
class Node < Capybara::Driver::Node
|
11
|
+
|
12
|
+
# @api capybara
|
13
|
+
# @return [String] the inner text of the node
|
14
|
+
def text
|
15
|
+
native.text
|
16
|
+
end
|
17
|
+
|
18
|
+
# @api capybara
|
19
|
+
# @param [String] name attribute name
|
20
|
+
# @return [String] the attribute value
|
21
|
+
def [](name)
|
22
|
+
name = name.to_s
|
23
|
+
case name
|
24
|
+
when 'checked'
|
25
|
+
native.checked?
|
26
|
+
else
|
27
|
+
native[name.to_s]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# @api capybara
|
32
|
+
# @return [String, Array<String>] the form element's value
|
33
|
+
def value
|
34
|
+
native.value
|
35
|
+
end
|
36
|
+
|
37
|
+
# Set the form element's value.
|
38
|
+
#
|
39
|
+
# @api capybara
|
40
|
+
# @param [String] value the form element's new value
|
41
|
+
def set(value)
|
42
|
+
if tag_name == 'textarea'
|
43
|
+
native.value = value.to_s
|
44
|
+
elsif tag_name == 'input' and type == 'radio'
|
45
|
+
click
|
46
|
+
elsif tag_name == 'input' and type == 'checkbox'
|
47
|
+
if value != self['checked']
|
48
|
+
click
|
49
|
+
end
|
50
|
+
elsif tag_name == 'input'
|
51
|
+
native.value = value.to_s
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# @api capybara
|
56
|
+
def select_option
|
57
|
+
#if it is already selected: do nothing
|
58
|
+
#if it isn't selected: click on it
|
59
|
+
native.click unless selected?
|
60
|
+
end
|
61
|
+
|
62
|
+
# Unselect an option from a select box.
|
63
|
+
#
|
64
|
+
# @api capybara
|
65
|
+
def unselect_option
|
66
|
+
unless select_node.multiple_select?
|
67
|
+
raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box."
|
68
|
+
end
|
69
|
+
|
70
|
+
native.unselect
|
71
|
+
end
|
72
|
+
|
73
|
+
# Click the element.
|
74
|
+
def click
|
75
|
+
native.click
|
76
|
+
end
|
77
|
+
|
78
|
+
# Drag the element on top of the target element.
|
79
|
+
#
|
80
|
+
# @api capybara
|
81
|
+
# @param [Node] element the target element
|
82
|
+
def drag_to(element)
|
83
|
+
trigger('mousedown')
|
84
|
+
element.trigger('mousemove')
|
85
|
+
element.trigger('mouseup')
|
86
|
+
end
|
87
|
+
|
88
|
+
# @api capybara
|
89
|
+
# @return [String] the element's tag name
|
90
|
+
def tag_name
|
91
|
+
native.tag_name
|
92
|
+
end
|
93
|
+
|
94
|
+
# @api capybara
|
95
|
+
# @return [true, false] the element's visiblity
|
96
|
+
def visible?
|
97
|
+
native.visible?
|
98
|
+
end
|
99
|
+
|
100
|
+
# @api capybara
|
101
|
+
# @return [true, false] the element's visiblity
|
102
|
+
def checked?
|
103
|
+
native.checked?
|
104
|
+
end
|
105
|
+
|
106
|
+
# @api capybara
|
107
|
+
# @return [true, false] the element's visiblity
|
108
|
+
def selected?
|
109
|
+
native.selected?
|
110
|
+
end
|
111
|
+
|
112
|
+
# @api capybara
|
113
|
+
# @return [String] the XPath to locate the node
|
114
|
+
def path
|
115
|
+
native.xpath
|
116
|
+
end
|
117
|
+
|
118
|
+
# Trigger an event on the element.
|
119
|
+
#
|
120
|
+
# @api capybara
|
121
|
+
# @param [String] event the event to trigger
|
122
|
+
def trigger(event)
|
123
|
+
native.fire_event(event.to_s)
|
124
|
+
end
|
125
|
+
|
126
|
+
# @api capybara
|
127
|
+
# @param [String] selector XPath query
|
128
|
+
# @return [Array<Node>] the matched nodes
|
129
|
+
def find(selector)
|
130
|
+
nodes = []
|
131
|
+
native.find(selector).each { |node| nodes << self.class.new(self, node) }
|
132
|
+
nodes
|
133
|
+
end
|
134
|
+
|
135
|
+
protected
|
136
|
+
|
137
|
+
# @return [true, false] whether the node allows multiple-option selection (if the node is a select).
|
138
|
+
def multiple_select?
|
139
|
+
tag_name == "select" && native.multiple_select?
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
# Return all child nodes which match the selector criteria.
|
145
|
+
#
|
146
|
+
# @api capybara
|
147
|
+
# @return [Array<Node>] the matched nodes
|
148
|
+
def all_unfiltered(selector)
|
149
|
+
nodes = []
|
150
|
+
native.find(selector).each { |node| nodes << self.class.new(driver, node) }
|
151
|
+
nodes
|
152
|
+
end
|
153
|
+
|
154
|
+
# @return [String] the node's type attribute
|
155
|
+
def type
|
156
|
+
native[:type]
|
157
|
+
end
|
158
|
+
|
159
|
+
# @return [Node] the select node, if this is an option node
|
160
|
+
def select_node
|
161
|
+
find('./ancestor::select').first
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
attr_reader :app, :rack_server, :options
|
166
|
+
|
167
|
+
# Creates a new instance of the Akephalos Driver for Capybara. The driver is
|
168
|
+
# registered with Capybara by a name, so that it can be chosen when
|
169
|
+
# Capybara's javascript_driver is changed. By default, Akephalos is
|
170
|
+
# registered like this:
|
171
|
+
#
|
172
|
+
# Capybara.register_driver :akephalos do |app|
|
173
|
+
# Capybara::Akephalos::Driver.new(
|
174
|
+
# app,
|
175
|
+
# :browser => :firefox_3_6,
|
176
|
+
# :validate_scripts => true
|
177
|
+
# )
|
178
|
+
# end
|
179
|
+
#
|
180
|
+
# @param app the Rack application to run
|
181
|
+
# @param [Hash] options the Akephalos configuration options
|
182
|
+
# @option options [Symbol] :browser (:firefox_3_6) the browser
|
183
|
+
# compatibility mode to run in. Available options:
|
184
|
+
# :firefox_3_6
|
185
|
+
# :firefox_3
|
186
|
+
# :ie6
|
187
|
+
# :ie7
|
188
|
+
# :ie8
|
189
|
+
#
|
190
|
+
# @option options [true, false] :validate_scripts (true) whether to raise
|
191
|
+
# exceptions on script errors
|
192
|
+
#
|
193
|
+
def initialize(app, options = {})
|
194
|
+
@app = app
|
195
|
+
@options = options
|
196
|
+
@rack_server = Capybara::Server.new(@app)
|
197
|
+
@rack_server.boot if Capybara.run_server
|
198
|
+
end
|
199
|
+
|
200
|
+
# Visit the given path in the browser.
|
201
|
+
#
|
202
|
+
# @param [String] path relative path to visit
|
203
|
+
def visit(path)
|
204
|
+
browser.visit(url(path))
|
205
|
+
end
|
206
|
+
|
207
|
+
# @return [String] the page's original source
|
208
|
+
def source
|
209
|
+
page.source
|
210
|
+
end
|
211
|
+
|
212
|
+
# @return [String] the page's modified source
|
213
|
+
# page.modified_source will return a string with
|
214
|
+
# html entities converted into the unicode equivalent
|
215
|
+
# but the string will be marked as ASCII-8BIT
|
216
|
+
# which causes conversion issues so we force the encoding
|
217
|
+
# to UTF-8 (ruby 1.9 only)
|
218
|
+
def body
|
219
|
+
body_source = page.modified_source
|
220
|
+
|
221
|
+
if body_source.respond_to?(:force_encoding)
|
222
|
+
body_source.force_encoding("UTF-8")
|
223
|
+
else
|
224
|
+
body_source
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
# @return [Hash{String => String}] the page's response headers
|
229
|
+
def response_headers
|
230
|
+
page.response_headers
|
231
|
+
end
|
232
|
+
|
233
|
+
# @return [Integer] the response's status code
|
234
|
+
def status_code
|
235
|
+
page.status_code
|
236
|
+
end
|
237
|
+
|
238
|
+
# Execute the given block within the context of a specified frame.
|
239
|
+
#
|
240
|
+
# @param [String] frame_id the frame's id or index
|
241
|
+
# @raise [Capybara::ElementNotFound] if the frame is not found
|
242
|
+
def within_frame(frame_id_or_index, &block)
|
243
|
+
result = page.within_frame(frame_id_or_index, &block)
|
244
|
+
unless page.within_frame(frame_id_or_index, &block)
|
245
|
+
raise Capybara::ElementNotFound, "Unable to find frame with id '#{frame_id_or_index}'"
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
# Clear all cookie session data.
|
250
|
+
# @deprecated This method is deprecated in Capybara's master branch. Use
|
251
|
+
# {#reset!} instead.
|
252
|
+
def cleanup!
|
253
|
+
reset!
|
254
|
+
end
|
255
|
+
|
256
|
+
# Reset session
|
257
|
+
def reset!
|
258
|
+
cookies.clear
|
259
|
+
browser.visit('about:blank')
|
260
|
+
end
|
261
|
+
|
262
|
+
# Confirm or cancel the dialog, returning the text of the dialog
|
263
|
+
def confirm_dialog(confirm = true, &block)
|
264
|
+
browser.confirm_dialog(confirm, &block)
|
265
|
+
end
|
266
|
+
|
267
|
+
# @return [String] the page's current URL
|
268
|
+
def current_url
|
269
|
+
page.current_url
|
270
|
+
end
|
271
|
+
|
272
|
+
# Search for nodes which match the given XPath selector.
|
273
|
+
#
|
274
|
+
# @param [String] selector XPath query
|
275
|
+
# @return [Array<Node>] the matched nodes
|
276
|
+
def find(selector)
|
277
|
+
nodes = []
|
278
|
+
page.find(selector).each { |node| nodes << Node.new(self, node) }
|
279
|
+
nodes
|
280
|
+
end
|
281
|
+
|
282
|
+
# Execute JavaScript against the current page, discarding any return value.
|
283
|
+
#
|
284
|
+
# @param [String] script the JavaScript to be executed
|
285
|
+
# @return [nil]
|
286
|
+
def execute_script(script)
|
287
|
+
page.execute_script script
|
288
|
+
end
|
289
|
+
|
290
|
+
# Execute JavaScript against the current page and return the results.
|
291
|
+
#
|
292
|
+
# @param [String] script the JavaScript to be executed
|
293
|
+
# @return the result of the JavaScript
|
294
|
+
def evaluate_script(script)
|
295
|
+
page.evaluate_script script
|
296
|
+
end
|
297
|
+
|
298
|
+
# @return the current page
|
299
|
+
def page
|
300
|
+
browser.page
|
301
|
+
end
|
302
|
+
|
303
|
+
# @return the browser
|
304
|
+
def browser
|
305
|
+
@browser ||= Akephalos::Client.new(@options)
|
306
|
+
end
|
307
|
+
|
308
|
+
# @return the session cookies
|
309
|
+
def cookies
|
310
|
+
browser.cookies
|
311
|
+
end
|
312
|
+
|
313
|
+
# @return [String] the current user agent string
|
314
|
+
def user_agent
|
315
|
+
browser.user_agent
|
316
|
+
end
|
317
|
+
|
318
|
+
# Set the User-Agent header for this session. If :default is given, the
|
319
|
+
# User-Agent header will be reset to the default browser's user agent.
|
320
|
+
#
|
321
|
+
# @param [:default] user_agent the default user agent
|
322
|
+
# @param [String] user_agent the user agent string to use
|
323
|
+
def user_agent=(user_agent)
|
324
|
+
browser.user_agent = user_agent
|
325
|
+
end
|
326
|
+
|
327
|
+
# Disable waiting in Capybara, since waiting is handled directly by
|
328
|
+
# Akephalos.
|
329
|
+
#
|
330
|
+
# @return [false]
|
331
|
+
def wait
|
332
|
+
false
|
333
|
+
end
|
334
|
+
|
335
|
+
private
|
336
|
+
|
337
|
+
# @param [String] path
|
338
|
+
# @return [String] the absolute URL for the given path
|
339
|
+
def url(path)
|
340
|
+
rack_server.url(path)
|
341
|
+
end
|
342
|
+
|
343
|
+
end
|
344
|
+
|
345
|
+
Capybara.register_driver :akephalos do |app|
|
346
|
+
Capybara::Driver::Akephalos.new(app)
|
347
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Akephalos
|
2
|
+
class Client
|
3
|
+
# Interface for working with HtmlUnit's CookieManager, providing a basic
|
4
|
+
# API for manipulating the cookies in a session.
|
5
|
+
class Cookies
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
# @param [HtmlUnit::CookieManager] cookie manager
|
9
|
+
def initialize(cookie_manager)
|
10
|
+
@cookie_manager = cookie_manager
|
11
|
+
end
|
12
|
+
|
13
|
+
# @param [name] the cookie name
|
14
|
+
# @return [Cookie] the cookie with the given name
|
15
|
+
# @return [nil] when no cookie is found
|
16
|
+
def [](name)
|
17
|
+
cookie = @cookie_manager.getCookie(name)
|
18
|
+
Cookie.new(cookie) if cookie
|
19
|
+
end
|
20
|
+
|
21
|
+
# Clears all cookies for this session.
|
22
|
+
def clear
|
23
|
+
@cookie_manager.clearCookies
|
24
|
+
end
|
25
|
+
|
26
|
+
# Iterator for all cookies in the current session.
|
27
|
+
def each
|
28
|
+
@cookie_manager.getCookies.each do |cookie|
|
29
|
+
yield Cookie.new(cookie)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Remove the cookie from the session.
|
34
|
+
#
|
35
|
+
# @param [Cookie] the cookie to remove
|
36
|
+
def delete(cookie)
|
37
|
+
@cookie_manager.removeCookie(cookie.native)
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [true, false] whether there are any cookies
|
41
|
+
def empty?
|
42
|
+
!any?
|
43
|
+
end
|
44
|
+
|
45
|
+
class Cookie
|
46
|
+
|
47
|
+
attr_reader :domain, :expires, :name, :path, :value
|
48
|
+
|
49
|
+
# @param [HtmlUnit::Cookie] the cookie
|
50
|
+
def initialize(cookie)
|
51
|
+
@_cookie = cookie
|
52
|
+
@domain = cookie.getDomain
|
53
|
+
@expires = cookie.getExpires
|
54
|
+
@name = cookie.getName
|
55
|
+
@path = cookie.getPath
|
56
|
+
@value = cookie.getValue
|
57
|
+
@secure = cookie.isSecure
|
58
|
+
end
|
59
|
+
|
60
|
+
def secure?
|
61
|
+
!!@secure
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [HtmlUnit::Cookie] the native cookie object
|
65
|
+
# @api private
|
66
|
+
def native
|
67
|
+
@_cookie
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module Akephalos
|
2
|
+
class Client
|
3
|
+
|
4
|
+
# Akephalos::Client::Filter extends HtmlUnit's WebConnectionWrapper to
|
5
|
+
# enable filtering outgoing requests generated interally by HtmlUnit.
|
6
|
+
#
|
7
|
+
# When a request comes through, it will be tested against the filters
|
8
|
+
# defined in Akephalos.filters and return a mock response if a match is
|
9
|
+
# found. If no filters are defined, or no filters match the request, then
|
10
|
+
# the response will bubble up to HtmlUnit for the normal request/response
|
11
|
+
# cycle.
|
12
|
+
class Filter < HtmlUnit::Util::WebConnectionWrapper
|
13
|
+
# Filters an outgoing request, and if a match is found, returns the mock
|
14
|
+
# response.
|
15
|
+
#
|
16
|
+
# @param [WebRequest] request the pending HTTP request
|
17
|
+
# @return [WebResponse] when the request matches a defined filter
|
18
|
+
# @return [nil] when no filters match the request
|
19
|
+
def filter(request)
|
20
|
+
if filter = find_filter(request)
|
21
|
+
start_time = Time.now
|
22
|
+
headers = filter[:headers].map do |name, value|
|
23
|
+
HtmlUnit::Util::NameValuePair.new(name.to_s, value.to_s)
|
24
|
+
end
|
25
|
+
response = HtmlUnit::WebResponseData.new(
|
26
|
+
filter[:body].to_s.to_java_bytes,
|
27
|
+
filter[:status],
|
28
|
+
HTTP_STATUS_CODES.fetch(filter[:status], "Unknown"),
|
29
|
+
headers
|
30
|
+
)
|
31
|
+
HtmlUnit::WebResponse.new(response, request, Time.now - start_time)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Searches for a filter which matches the request's HTTP method and url.
|
36
|
+
#
|
37
|
+
# @param [WebRequest] request the pending HTTP request
|
38
|
+
# @return [Hash] when a filter matches the request
|
39
|
+
# @return [nil] when no filters match the request
|
40
|
+
def find_filter(request)
|
41
|
+
Akephalos.filters.find do |filter|
|
42
|
+
request.http_method === filter[:method] && request.url.to_s =~ filter[:filter]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# This method is called by WebClient when a page is requested, and will
|
47
|
+
# return a mock response if the request matches a defined filter or else
|
48
|
+
# return the actual response.
|
49
|
+
#
|
50
|
+
# @api htmlunit
|
51
|
+
# @param [WebRequest] request the pending HTTP request
|
52
|
+
# @return [WebResponse]
|
53
|
+
def getResponse(request)
|
54
|
+
filter(request) || super
|
55
|
+
end
|
56
|
+
|
57
|
+
# Map of status codes to their English descriptions.
|
58
|
+
HTTP_STATUS_CODES = {
|
59
|
+
100 => "Continue",
|
60
|
+
101 => "Switching Protocols",
|
61
|
+
102 => "Processing",
|
62
|
+
200 => "OK",
|
63
|
+
201 => "Created",
|
64
|
+
202 => "Accepted",
|
65
|
+
203 => "Non-Authoritative Information",
|
66
|
+
204 => "No Content",
|
67
|
+
205 => "Reset Content",
|
68
|
+
206 => "Partial Content",
|
69
|
+
207 => "Multi-Status",
|
70
|
+
300 => "Multiple Choices",
|
71
|
+
301 => "Moved Permanently",
|
72
|
+
302 => "Found",
|
73
|
+
303 => "See Other",
|
74
|
+
304 => "Not Modified",
|
75
|
+
305 => "Use Proxy",
|
76
|
+
306 => "Switch Proxy",
|
77
|
+
307 => "Temporary Redirect",
|
78
|
+
400 => "Bad Request",
|
79
|
+
401 => "Unauthorized",
|
80
|
+
402 => "Payment Required",
|
81
|
+
403 => "Forbidden",
|
82
|
+
404 => "Not Found",
|
83
|
+
405 => "Method Not Allowed",
|
84
|
+
406 => "Not Acceptable",
|
85
|
+
407 => "Proxy Authentication Required",
|
86
|
+
408 => "Request Timeout",
|
87
|
+
409 => "Conflict",
|
88
|
+
410 => "Gone",
|
89
|
+
411 => "Length Required",
|
90
|
+
412 => "Precondition Failed",
|
91
|
+
413 => "Request Entity Too Large",
|
92
|
+
414 => "Request-URI Too Long",
|
93
|
+
415 => "Unsupported Media Type",
|
94
|
+
416 => "Requested Range Not Satisfiable",
|
95
|
+
417 => "Expectation Failed",
|
96
|
+
418 => "I'm a teapot",
|
97
|
+
421 => "There are too many connections from your internet address",
|
98
|
+
422 => "Unprocessable Entity",
|
99
|
+
423 => "Locked",
|
100
|
+
424 => "Failed Dependency",
|
101
|
+
425 => "Unordered Collection",
|
102
|
+
426 => "Upgrade Required",
|
103
|
+
449 => "Retry With",
|
104
|
+
450 => "Blocked by Windows Parental Controls",
|
105
|
+
500 => "Internal Server Error",
|
106
|
+
501 => "Not Implemented",
|
107
|
+
502 => "Bad Gateway",
|
108
|
+
503 => "Service Unavailable",
|
109
|
+
504 => "Gateway Timeout",
|
110
|
+
505 => "HTTP Version Not Supported",
|
111
|
+
506 => "Variant Also Negotiates",
|
112
|
+
507 => "Insufficient Storage",
|
113
|
+
509 => "Bandwidth Limit Exceeded",
|
114
|
+
510 => "Not Extended",
|
115
|
+
530 => "User access denied"
|
116
|
+
}.freeze
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|