# frozen_string_literal: true
module Excon
# Excon exception classes
class Error < StandardError
@default_status_error = :HTTPStatus
class StubNotFound < Error; end
class InvalidStub < Error; end
class Warning < Error; end
# Socket related errors
class Socket < Error
attr_reader :socket_error
def initialize(socket_error = Excon::Error.new)
if is_a?(Certificate) || is_a?(Excon::Errors::CertificateError)
super
else
super("#{socket_error.message} (#{socket_error.class})")
set_backtrace(socket_error.backtrace)
@socket_error = socket_error
end
end
end
# Certificate related errors
class Certificate < Socket
def initialize(socket_error = Excon::Error.new)
msg = <<-EOL
Unable to verify certificate. This may be an issue with the remote host or with Excon. Excon has certificates bundled, but these can be customized:
`Excon.defaults[:ssl_ca_path] = path_to_certs`
`ENV['SSL_CERT_DIR'] = path_to_certs`
`Excon.defaults[:ssl_ca_file] = path_to_file`
`ENV['SSL_CERT_FILE'] = path_to_file`
`Excon.defaults[:ssl_verify_callback] = callback`
(see OpenSSL::SSL::SSLContext#verify_callback)
or:
`Excon.defaults[:ssl_verify_peer] = false` (less secure).
EOL
full_message = "#{socket_error.message} (#{socket_error.class})" +
' ' + msg
super(full_message)
set_backtrace(socket_error.backtrace)
@socket_error = socket_error
end
end
class InvalidHeaderKey < Error; end
class InvalidHeaderValue < Error; end
class Timeout < Error; end
class ResponseParse < Error; end
class ProxyConnectionError < Error
attr_reader :request, :response
def initialize(msg, request = nil, response = nil)
super(msg)
@request = request
@response = response
end
end
class ProxyParse < Error; end
class TooManyRedirects < Error; end
# Base class for HTTP Error classes
class HTTPStatus < Error
attr_reader :request, :response
def initialize(msg, request = nil, response = nil)
super(msg)
@request = request
@response = response
end
end
# HTTP Error classes
class Informational < HTTPStatus; end
class Success < HTTPStatus; end
class Redirection < HTTPStatus; end
class Client < HTTPStatus; end
class Server < HTTPStatus; end
class Continue < Informational; end # 100
class SwitchingProtocols < Informational; end # 101
class OK < Success; end # 200
class Created < Success; end # 201
class Accepted < Success; end # 202
class NonAuthoritativeInformation < Success; end # 203
class NoContent < Success; end # 204
class ResetContent < Success; end # 205
class PartialContent < Success; end # 206
class MultipleChoices < Redirection; end # 300
class MovedPermanently < Redirection; end # 301
class Found < Redirection; end # 302
class SeeOther < Redirection; end # 303
class NotModified < Redirection; end # 304
class UseProxy < Redirection; end # 305
class TemporaryRedirect < Redirection; end # 307
class BadRequest < Client; end # 400
class Unauthorized < Client; end # 401
class PaymentRequired < Client; end # 402
class Forbidden < Client; end # 403
class NotFound < Client; end # 404
class MethodNotAllowed < Client; end # 405
class NotAcceptable < Client; end # 406
class ProxyAuthenticationRequired < Client; end # 407
class RequestTimeout < Client; end # 408
class Conflict < Client; end # 409
class Gone < Client; end # 410
class LengthRequired < Client; end # 411
class PreconditionFailed < Client; end # 412
class RequestEntityTooLarge < Client; end # 413
class RequestURITooLong < Client; end # 414
class UnsupportedMediaType < Client; end # 415
class RequestedRangeNotSatisfiable < Client; end # 416
class ExpectationFailed < Client; end # 417
class UnprocessableEntity < Client; end # 422
class TooManyRequests < Client; end # 429
class InternalServerError < Server; end # 500
class NotImplemented < Server; end # 501
class BadGateway < Server; end # 502
class ServiceUnavailable < Server; end # 503
class GatewayTimeout < Server; end # 504
def self.status_errors
@status_errors ||= {
100 => [Excon::Error::Continue, 'Continue'],
101 => [Excon::Error::SwitchingProtocols, 'Switching Protocols'],
200 => [Excon::Error::OK, 'OK'],
201 => [Excon::Error::Created, 'Created'],
202 => [Excon::Error::Accepted, 'Accepted'],
203 => [Excon::Error::NonAuthoritativeInformation, 'Non-Authoritative Information'],
204 => [Excon::Error::NoContent, 'No Content'],
205 => [Excon::Error::ResetContent, 'Reset Content'],
206 => [Excon::Error::PartialContent, 'Partial Content'],
300 => [Excon::Error::MultipleChoices, 'Multiple Choices'],
301 => [Excon::Error::MovedPermanently, 'Moved Permanently'],
302 => [Excon::Error::Found, 'Found'],
303 => [Excon::Error::SeeOther, 'See Other'],
304 => [Excon::Error::NotModified, 'Not Modified'],
305 => [Excon::Error::UseProxy, 'Use Proxy'],
307 => [Excon::Error::TemporaryRedirect, 'Temporary Redirect'],
400 => [Excon::Error::BadRequest, 'Bad Request'],
401 => [Excon::Error::Unauthorized, 'Unauthorized'],
402 => [Excon::Error::PaymentRequired, 'Payment Required'],
403 => [Excon::Error::Forbidden, 'Forbidden'],
404 => [Excon::Error::NotFound, 'Not Found'],
405 => [Excon::Error::MethodNotAllowed, 'Method Not Allowed'],
406 => [Excon::Error::NotAcceptable, 'Not Acceptable'],
407 => [Excon::Error::ProxyAuthenticationRequired, 'Proxy Authentication Required'],
408 => [Excon::Error::RequestTimeout, 'Request Timeout'],
409 => [Excon::Error::Conflict, 'Conflict'],
410 => [Excon::Error::Gone, 'Gone'],
411 => [Excon::Error::LengthRequired, 'Length Required'],
412 => [Excon::Error::PreconditionFailed, 'Precondition Failed'],
413 => [Excon::Error::RequestEntityTooLarge, 'Request Entity Too Large'],
414 => [Excon::Error::RequestURITooLong, 'Request-URI Too Long'],
415 => [Excon::Error::UnsupportedMediaType, 'Unsupported Media Type'],
416 => [Excon::Error::RequestedRangeNotSatisfiable, 'Request Range Not Satisfiable'],
417 => [Excon::Error::ExpectationFailed, 'Expectation Failed'],
422 => [Excon::Error::UnprocessableEntity, 'Unprocessable Entity'],
429 => [Excon::Error::TooManyRequests, 'Too Many Requests'],
500 => [Excon::Error::InternalServerError, 'InternalServerError'],
501 => [Excon::Error::NotImplemented, 'Not Implemented'],
502 => [Excon::Error::BadGateway, 'Bad Gateway'],
503 => [Excon::Error::ServiceUnavailable, 'Service Unavailable'],
504 => [Excon::Error::GatewayTimeout, 'Gateway Timeout']
}
end
# Messages for nicer exceptions, from rfc2616
def self.status_error(request, response)
error_class, error_message = status_errors[response[:status]]
if error_class.nil?
default_class = Excon::Error.const_get(@default_status_error)
error_class, error_message = [default_class, 'Unknown']
end
message = StringIO.new
str = "Expected(#{request[:expects].inspect}) <=>" +
" Actual(#{response[:status]} #{error_message})"
message.puts(str)
if request[:debug_request]
message.puts('excon.error.request')
Excon::PrettyPrinter.pp(message, request)
end
if request[:debug_response]
message.puts('excon.error.response')
Excon::PrettyPrinter.pp(message, response.data)
end
message.rewind
error_class.new(message.read, request, response)
end
end
# Legacy
module Errors
Excon::Errors::Error = Excon::Error
legacy_re = /
\A
Client
|Server
|Socket
|Certificate
|HTTPStatus
|InternalServer
\Z
/x
klasses = Excon::Error.constants.select do |c|
Excon::Error.const_get(c).is_a? Class
end
klasses.each do |klass|
class_name = klass.to_s
unless class_name =~ /Error\Z/
class_name = klass.to_s + 'Error' if class_name =~ legacy_re
end
Excon::Errors.const_set(class_name, Excon::Error.const_get(klass))
end
def self.status_error(request, response)
Excon::Error.status_error(request, response)
end
end
end