Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions lib/http/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,52 @@ def initialize(response)
end
end

# Client errors 4xx
class ClientError < StatusError; end
class BadRequestError < ClientError; end
class UnauthorizedError < ClientError; end
class PaymentRequiredError < ClientError; end
class ForbiddenError < ClientError; end
class NotFoundError < ClientError; end
class MethodNotAllowedError < ClientError; end
class NotAcceptableError < ClientError; end
class ProxyAuthenticationRequiredError < ClientError; end
class RequestTimeoutError < ClientError; end
class ConflictError < ClientError; end
class GoneError < ClientError; end
class LengthRequiredError < ClientError; end
class PreconditionFailedError < ClientError; end
class ContentTooLargeError < ClientError; end
class UriTooLongError < ClientError; end
class UnsupportedMediaTypeError < ClientError; end
class RangeNotSatisfiableError < ClientError; end
class ExpectationFailedError < ClientError; end
class ImATeapotError < ClientError; end
class MisdirectedRequestError < ClientError; end
class UnprocessableContentError < ClientError; end
class LockedError < ClientError; end
class FailedDependencyError < ClientError; end
class TooEarlyError < ClientError; end
class UpgradeRequiredError < ClientError; end
class PreconditionRequiredError < ClientError; end
class TooManyRequestsError < ClientError; end
class RequestHeaderFieldsTooLargeError < ClientError; end
class UnavailableForLegalReasonsError < ClientError; end

# Server errors 5xx
class ServerError < StatusError; end
class InternalServerError < ServerError; end
class NotImplementedError < ServerError; end
class BadGatewayError < ServerError; end
class ServiceUnavailableError < ServerError; end
class GatewayTimeoutError < ServerError; end
class HttpVersionNotSupportedError < ServerError; end
class VariantAlsoNegotiatesError < ServerError; end
class InsufficientStorageError < ServerError; end
class LoopDetectedError < ServerError; end
class NotExtendedError < ServerError; end
class NetworkAuthenticationRequiredError < ServerError; end

# Raised when `Response#parse` fails due to any underlying reason (unexpected
# MIME type, or decoder fails). See `Exception#cause` for the original exception.
class ParseError < ResponseError; end
Expand Down
49 changes: 48 additions & 1 deletion lib/http/features/raise_error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,54 @@ def wrap_response(response)
return response if response.code < 400
return response if @ignore.include?(response.code)

raise StatusError, response
error_class =
case response.code
when 400 then BadRequestError
when 401 then UnauthorizedError
when 402 then PaymentRequiredError
when 403 then ForbiddenError
when 404 then NotFoundError
when 405 then MethodNotAllowedError
when 406 then NotAcceptableError
when 407 then ProxyAuthenticationRequiredError
when 408 then RequestTimeoutError
when 409 then ConflictError
when 410 then GoneError
when 411 then LengthRequiredError
when 412 then PreconditionFailedError
when 413 then ContentTooLargeError
when 414 then UriTooLongError
when 415 then UnsupportedMediaTypeError
when 416 then RangeNotSatisfiableError
when 417 then ExpectationFailedError
when 418 then ImATeapotError
when 421 then MisdirectedRequestError
when 422 then UnprocessableContentError
when 423 then LockedError
when 424 then FailedDependencyError
when 425 then TooEarlyError
when 426 then UpgradeRequiredError
when 428 then PreconditionRequiredError
when 429 then TooManyRequestsError
when 431 then RequestHeaderFieldsTooLargeError
when 451 then UnavailableForLegalReasonsError
when 400...500 then ClientError # Generic client error if the 4xx code is unmapped.
when 500 then InternalServerError
when 501 then NotImplementedError
when 502 then BadGatewayError
when 503 then ServiceUnavailableError
when 504 then GatewayTimeoutError
when 505 then HttpVersionNotSupportedError
when 506 then VariantAlsoNegotiatesError
when 507 then InsufficientStorageError
when 508 then LoopDetectedError
when 510 then NotExtendedError
when 511 then NetworkAuthenticationRequiredError
when 500...600 then ServerError # Generic server error if the 5xx code is unmapped.
else StatusError
end

raise error_class, response
end

HTTP::Options.register_feature(:raise_error, self)
Expand Down
28 changes: 17 additions & 11 deletions test/http/features/raise_error_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,26 +37,32 @@ def test_wrap_response_when_status_is_399_returns_original_response
assert_same response, result
end

def test_wrap_response_when_status_is_400_raises
def test_wrap_response_when_status_is_400_raises_bad_request_error
feature = HTTP::Features::RaiseError.new(ignore: [])
response = build_response(status: 400)
err = assert_raises(HTTP::StatusError) { feature.wrap_response(response) }
err = assert_raises(HTTP::BadRequestError) { feature.wrap_response(response) }
assert_equal "Unexpected status code 400", err.message
end

def test_wrap_response_when_status_is_599_raises
def test_wrap_response_when_status_is_500_raises_internal_server_error
feature = HTTP::Features::RaiseError.new(ignore: [])
response = build_response(status: 599)
err = assert_raises(HTTP::StatusError) { feature.wrap_response(response) }
assert_equal "Unexpected status code 599", err.message
response = build_response(status: 500)
err = assert_raises(HTTP::InternalServerError) { feature.wrap_response(response) }
assert_equal "Unexpected status code 500", err.message
end

def test_wrap_response_when_error_status_is_ignored_returns_original_response
feature = HTTP::Features::RaiseError.new(ignore: [500])
response = build_response(status: 500)
result = feature.wrap_response(response)
def test_wrap_response_when_unmapped_4xx_status_raises_client_error
feature = HTTP::Features::RaiseError.new(ignore: [])
response = build_response(status: 499)
err = assert_raises(HTTP::ClientError) { feature.wrap_response(response) }
assert_equal "Unexpected status code 499", err.message
end

assert_same response, result
def test_wrap_response_when_unmapped_5xx_status_raises_server_error
feature = HTTP::Features::RaiseError.new(ignore: [])
response = build_response(status: 599)
err = assert_raises(HTTP::ServerError) { feature.wrap_response(response) }
assert_equal "Unexpected status code 599", err.message
end

# -- #initialize --
Expand Down
Loading