diff --git a/lib/http/chainable.rb b/lib/http/chainable.rb index 188f6c37..55db648d 100644 --- a/lib/http/chainable.rb +++ b/lib/http/chainable.rb @@ -243,6 +243,7 @@ def nodelay # * instrumentation # * logging # * normalize_uri + # * raise_error # @param features def use(*features) branch default_options.with_features(features) diff --git a/lib/http/errors.rb b/lib/http/errors.rb index 8f6c2061..dcbe5a13 100644 --- a/lib/http/errors.rb +++ b/lib/http/errors.rb @@ -21,6 +21,17 @@ class ResponseError < Error; end # Requested to do something when we're in the wrong state class StateError < ResponseError; end + # When status code indicates an error + class StatusError < ResponseError + attr_reader :response + + def initialize(response) + @response = response + + super("Unexpected status code #{response.code}") + end + end + # Generic Timeout error class TimeoutError < Error; end diff --git a/lib/http/feature.rb b/lib/http/feature.rb index b178becf..0a7e9ad1 100644 --- a/lib/http/feature.rb +++ b/lib/http/feature.rb @@ -16,6 +16,7 @@ def on_error(_request, _error); end require "http/features/auto_inflate" require "http/features/auto_deflate" -require "http/features/logging" require "http/features/instrumentation" +require "http/features/logging" require "http/features/normalize_uri" +require "http/features/raise_error" diff --git a/lib/http/features/raise_error.rb b/lib/http/features/raise_error.rb new file mode 100644 index 00000000..7de13b9e --- /dev/null +++ b/lib/http/features/raise_error.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module HTTP + module Features + class RaiseError < Feature + def initialize(ignore: []) + super() + + @ignore = ignore + end + + def wrap_response(response) + return response if response.code < 400 + return response if @ignore.include?(response.code) + + raise HTTP::StatusError, response + end + + HTTP::Options.register_feature(:raise_error, self) + end + end +end diff --git a/spec/lib/http/features/raise_error_spec.rb b/spec/lib/http/features/raise_error_spec.rb new file mode 100644 index 00000000..b279770a --- /dev/null +++ b/spec/lib/http/features/raise_error_spec.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +RSpec.describe HTTP::Features::RaiseError do + subject(:feature) { described_class.new(ignore: ignore) } + + let(:connection) { double } + let(:status) { 200 } + let(:ignore) { [] } + + describe "#wrap_response" do + subject(:result) { feature.wrap_response(response) } + + let(:response) do + HTTP::Response.new( + version: "1.1", + status: status, + headers: {}, + connection: connection, + request: HTTP::Request.new(verb: :get, uri: "https://example.com") + ) + end + + context "when status is 200" do + it "returns original request" do + expect(result).to be response + end + end + + context "when status is 399" do + let(:status) { 399 } + + it "returns original request" do + expect(result).to be response + end + end + + context "when status is 400" do + let(:status) { 400 } + + it "raises" do + expect { result }.to raise_error(HTTP::StatusError, "Unexpected status code 400") + end + end + + context "when status is 599" do + let(:status) { 599 } + + it "raises" do + expect { result }.to raise_error(HTTP::StatusError, "Unexpected status code 599") + end + end + + context "when error status is ignored" do + let(:status) { 500 } + let(:ignore) { [500] } + + it "returns original request" do + expect(result).to be response + end + end + end +end