Exception: X::HTTPError
- Includes:
- RequestContext, ResponseHeaders
- Defined in:
- x-core/lib/x/core/errors/http_error.rb
Overview
Base class for HTTP errors from the X API
The message is what the API said went wrong, read from the body of the response, behind the method and path of the request it answered. #body holds that body as it arrived, #headers the headers it came with, and #problem the problem it describes the failure with as a whole, and #problems each problem it names, for code that acts on the reason rather than logging it. #http_method and #uri are the request the API refused.
A 4xx raises a ClientError, a 5xx a ServerError, and a body that is not JSON an InvalidResponse, each a subclass of this. It is raised itself for a redirect the client does not follow: a 300 Multiple Choices, 304 Not Modified, or 305 Use Proxy, and a redirect whose Location is missing, is not a valid URL, or is not an HTTP or HTTPS URL.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#http_response ⇒ Net::HTTPResponse
readonly
The response itself, as the client received it.
-
#problem ⇒ Problem?
readonly
The problem the API described the failure with as a whole.
-
#problems ⇒ Array<Problem>
readonly
The problems the API named in the body of the response.
Instance Method Summary collapse
-
#body ⇒ String?
The body of the response, as it arrived.
-
#headers ⇒ Hash{String => String}
The headers of the response.
-
#http_method ⇒ Symbol?
The HTTP method the request was sent with.
-
#initialize(message = nil, http_response: nil, status: nil, headers: nil, body: nil, http_method: nil, uri: nil) ⇒ HTTPError
constructor
Initialize a new HTTPError.
-
#retry_after ⇒ Integer?
The seconds the response asks a request to wait before it is sent again.
-
#status ⇒ Integer
The HTTP status code, as an Integer like X::Response#status.
-
#uri ⇒ URI::Generic?
The URI the request was sent to.
Constructor Details
#initialize(message = nil, http_response: nil, status: nil, headers: nil, body: nil, http_method: nil, uri: nil) ⇒ HTTPError
Initialize a new HTTPError
Public, so that code that rescues an HTTPError, or a subclass such as NotFound, can be tested with one built from the status, headers, and body of a response, or from a Net::HTTP response, as x-core builds each from the response it parses. The error names the request, when given its method and URI, as x-core names the request the response answers.
It can be raised as any other exception is, as in raise X::NotFound, or raise X::NotFound, "gone", for a test double that stands in for a client: an error of a status, such as NotFound, given neither a response nor a status, is built with the status x-core raises it for, a ClientError or ServerError with the first of its kind, 400 or 500, and an InvalidResponse with 200. An HTTPError itself, which is raised for a status of any kind, must be given one. A message given is the message of the error, in place of the one read from the body.
143 144 145 146 147 148 149 150 151 152 |
# File 'x-core/lib/x/core/errors/http_error.rb', line 143 def initialize( = nil, http_response: nil, status: nil, headers: nil, body: nil, http_method: nil, uri: nil) @http_response = built_response(http_response, status:, headers:, body:) name_request(http_method, uri) parsed = parsed_body errors = Problem.all_from(parsed) described = (Problem.new(parsed) if describes_problem?(parsed)) @problems = errors.empty? ? [described].compact.freeze : errors @problem = described || errors.first super(( || (parsed) || @http_response.)) end |
Instance Attribute Details
#http_response ⇒ Net::HTTPResponse (readonly)
The response itself, as the client received it
It is an escape hatch, for what the error does not read: the status is #status, the headers are #headers, and the body is #body. It is the response of the transport the client sent the request with, a Net::HTTPResponse today, or the one built of the status, headers, and body the error was given. Its class is not covered by the compatibility promise of 1.x: a later release of 1.x may send its requests with another library, whose response this then returns.
79 80 81 |
# File 'x-core/lib/x/core/errors/http_error.rb', line 79 def http_response @http_response end |
#problem ⇒ Problem? (readonly)
The problem the API described the failure with as a whole
It is the problem the body of the response describes itself, by its title, detail, type, and status, whether or not the body names errors of its own, which are #problems, so its type reads the kind of failure alike for a request the API refused a parameter of and for one it refused to authorize; its attributes are the whole body. A body that describes no problem of its own, but names errors, as the responses of v1.1 do, is described by the first of them.
106 107 108 |
# File 'x-core/lib/x/core/errors/http_error.rb', line 106 def problem @problem end |
#problems ⇒ Array<Problem> (readonly)
The problems the API named in the body of the response
They are the errors the body names, such as each parameter of the request the API refused, or else the problem the body describes itself, which is #problem.
90 91 92 |
# File 'x-core/lib/x/core/errors/http_error.rb', line 90 def problems @problems end |
Instance Method Details
#body ⇒ String?
The body of the response, as it arrived
A server can send any body with an error, so it is the JSON the API describes a failure with, or whatever else was sent in its place, such as the page of a proxy. It is tagged UTF-8, the encoding of the JSON the API sends, and a body that is not valid UTF-8 keeps its bytes, so valid_encoding? tells it apart.
191 |
# File 'x-core/lib/x/core/errors/http_error.rb', line 191 def body = http_response.body |
#headers ⇒ Hash{String => String}
The headers of the response
The names are lowercase, and a field the API sent more than once is joined with a comma.
|
|
# File 'x-core/lib/x/core/errors/http_error.rb', line 29
|
#http_method ⇒ Symbol?
The HTTP method the request was sent with
|
|
# File 'x-core/lib/x/core/errors/http_error.rb', line 29
|
#retry_after ⇒ Integer?
The seconds the response asks a request to wait before it is sent again
The API sends a Retry-After header with a request it refused for a rate limit, and with some of the responses of a failure of its own, such as a 503 that names the time its endpoint is expected back. The header counts the seconds from when the response was sent, or names the time to wait until. A client waits it out before it sends an idempotent request refused for a failure of the API's own again, up to a minute, and before it sends any request refused for a rate limit again, up to max_rate_limit_wait; see Client#initialize.
205 206 207 208 209 210 |
# File 'x-core/lib/x/core/errors/http_error.rb', line 205 def retry_after value = http_response[RETRY_AFTER_HEADER] return if value.nil? value.match?(RETRY_AFTER_SECONDS) ? Integer(value, 10) : seconds_until(value) end |
#status ⇒ Integer
The HTTP status code, as an Integer like X::Response#status
179 |
# File 'x-core/lib/x/core/errors/http_error.rb', line 179 def status = Integer(http_response.code) |
#uri ⇒ URI::Generic?
The URI the request was sent to
|
|
# File 'x-core/lib/x/core/errors/http_error.rb', line 29
|