diff --git a/lib/http/errors.rb b/lib/http/errors.rb index 5ff410df..2d0821fa 100644 --- a/lib/http/errors.rb +++ b/lib/http/errors.rb @@ -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 diff --git a/lib/http/features/raise_error.rb b/lib/http/features/raise_error.rb index 9236a61f..01886ce3 100644 --- a/lib/http/features/raise_error.rb +++ b/lib/http/features/raise_error.rb @@ -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) diff --git a/test/http/features/raise_error_test.rb b/test/http/features/raise_error_test.rb index c14dda65..52767a01 100644 --- a/test/http/features/raise_error_test.rb +++ b/test/http/features/raise_error_test.rb @@ -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 --