diff --git a/lib/httpx/plugins/circuit_breaker.rb b/lib/httpx/plugins/circuit_breaker.rb index 52f58846..2ac33a7e 100644 --- a/lib/httpx/plugins/circuit_breaker.rb +++ b/lib/httpx/plugins/circuit_breaker.rb @@ -51,13 +51,13 @@ module HTTPX # run all requests through the circuit breaker, see if the circuit is # open for any of them. - real_requests = requests.each_with_object([]) do |req, real_reqs| + real_requests = requests.each_with_index.with_object([]) do |(req, idx), real_reqs| short_circuit_response = @circuit_store.try_respond(req) if short_circuit_response.nil? real_reqs << req next end - short_circuit_responses[requests.index(req)] = short_circuit_response + short_circuit_responses[idx] = short_circuit_response end # run requests for the remainder @@ -90,6 +90,7 @@ module HTTPX @circuit_store.try_open(request.uri, response) else @circuit_store.try_close(request.uri) + nil end end end diff --git a/lib/httpx/plugins/circuit_breaker/circuit_store.rb b/lib/httpx/plugins/circuit_breaker/circuit_store.rb index 707bee45..b04c7a61 100644 --- a/lib/httpx/plugins/circuit_breaker/circuit_store.rb +++ b/lib/httpx/plugins/circuit_breaker/circuit_store.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "mutex_m" + module HTTPX::Plugins::CircuitBreaker using HTTPX::URIExtensions @@ -13,18 +15,21 @@ module HTTPX::Plugins::CircuitBreaker options.circuit_breaker_half_open_drip_rate ) end + @circuits.extend(Mutex_m) end def try_open(uri, response) - circuit = get_circuit_for_uri(uri) + circuit = @circuits.synchronize { get_circuit_for_uri(uri) } circuit.try_open(response) end def try_close(uri) - return unless @circuits.key?(uri.origin) || @circuits.key?(uri.to_s) + circuit = @circuits.synchronize do + return unless @circuits.key?(uri.origin) || @circuits.key?(uri.to_s) - circuit = get_circuit_for_uri(uri) + get_circuit_for_uri(uri) + end circuit.try_close end @@ -32,7 +37,7 @@ module HTTPX::Plugins::CircuitBreaker # if circuit is open, it'll respond with the stored response. # if not, nil. def try_respond(request) - circuit = get_circuit_for_uri(request.uri) + circuit = @circuits.synchronize { get_circuit_for_uri(request.uri) } circuit.respond end @@ -40,9 +45,7 @@ module HTTPX::Plugins::CircuitBreaker private def get_circuit_for_uri(uri) - uri = URI(uri) - - if @circuits.key?(uri.origin) + if uri.respond_to?(:origin) && @circuits.key?(uri.origin) @circuits[uri.origin] else @circuits[uri.to_s] diff --git a/sig/httpx.rbs b/sig/httpx.rbs index d1e84440..f8b61851 100644 --- a/sig/httpx.rbs +++ b/sig/httpx.rbs @@ -5,7 +5,8 @@ module HTTPX VERSION: String - type uri = URI::HTTP | URI::HTTPS | string + type http_uri = URI::HTTP | URI::HTTPS + type uri = http_uri | string type generic_uri = String | URI::Generic type verb = "OPTIONS" | "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "TRACE" | "CONNECT" | diff --git a/sig/io/unix.rbs b/sig/io/unix.rbs index 29d2466c..e6bb7c7d 100644 --- a/sig/io/unix.rbs +++ b/sig/io/unix.rbs @@ -5,6 +5,6 @@ module HTTPX alias host path - def initialize: (URI::HTTP | URI::HTTPS origin, Array[String]? addresses, options options) -> void + def initialize: (http_uri origin, Array[String]? addresses, options options) -> void end end diff --git a/sig/plugins/circuit_breaker.rbs b/sig/plugins/circuit_breaker.rbs index 86611b59..5ec91b3b 100644 --- a/sig/plugins/circuit_breaker.rbs +++ b/sig/plugins/circuit_breaker.rbs @@ -3,17 +3,17 @@ module HTTPX module CircuitBreaker class CircuitStore - @circuits: Hash[String, Circuit] + @circuits: Hash[String, Circuit] & Mutex_m - def try_open: (generic_uri uri, response response) -> response? + def try_open: (uri uri, response response) -> response? def try_respond: (Request request) -> response? - def try_close: (generic_uri uri) -> void + def try_close: (http_uri uri) -> void private - def get_circuit_for_uri: (generic_uri uri) -> Circuit + def get_circuit_for_uri: (uri uri) -> Circuit def initialize: (Options & _CircuitOptions options) -> void end diff --git a/sig/plugins/proxy/ssh.rbs b/sig/plugins/proxy/ssh.rbs index 5976a25c..b37c5ac4 100644 --- a/sig/plugins/proxy/ssh.rbs +++ b/sig/plugins/proxy/ssh.rbs @@ -7,7 +7,7 @@ module HTTPX module InstanceMethods private - def build_gateway_socket: (int, URI::HTTP | URI::HTTPS, Options) -> _ToIO + def build_gateway_socket: (int, http_uri, Options) -> _ToIO end end end diff --git a/sig/plugins/response_cache.rbs b/sig/plugins/response_cache.rbs index d0a048e5..860d0d46 100644 --- a/sig/plugins/response_cache.rbs +++ b/sig/plugins/response_cache.rbs @@ -2,9 +2,10 @@ module HTTPX module Plugins module ResponseCache CACHEABLE_VERBS: Array[verb] + CACHEABLE_STATUS_CODES: Array[Integer] - def self?.cacheable_request?: (Request request) -> bool - def self?.cacheable_response?: (response response) -> bool + def self?.cacheable_request?: (Request & RequestMethods request) -> bool + def self?.cacheable_response?: (::HTTPX::ErrorResponse | (Response & ResponseMethods) response) -> bool def self?.cached_response?: (response response) -> bool class Store @@ -34,6 +35,8 @@ module HTTPX end module RequestMethods + @response_cache_key: String + def response_cache_key: () -> String end diff --git a/sig/request.rbs b/sig/request.rbs index 19ceb876..3f0fc0b2 100644 --- a/sig/request.rbs +++ b/sig/request.rbs @@ -7,7 +7,7 @@ module HTTPX USER_AGENT: String attr_reader verb: verb - attr_reader uri: URI::HTTP | URI::HTTPS + attr_reader uri: http_uri attr_reader headers: Headers attr_reader body: Body attr_reader state: Symbol diff --git a/test/support/ci/build.sh b/test/support/ci/build.sh index 991224d8..0cd7b72f 100755 --- a/test/support/ci/build.sh +++ b/test/support/ci/build.sh @@ -65,7 +65,7 @@ if [[ "$RUBY_ENGINE" = "ruby" ]] && [[ ${RUBY_VERSION:0:1} = "3" ]] && [[ ! $RUB export RUBYOPT="$RUBYOPT -rbundler/setup -rrbs/test/setup" export RBS_TEST_RAISE=true export RBS_TEST_LOGLEVEL=error - export RBS_TEST_OPT="-Isig -rset -rmutex_m -rforwardable -ruri -rjson -ripaddr -rpathname -rtime -rtimeout -rresolv -rsocket -ropenssl -rbase64 -rzlib -rcgi -rhttp-2-next" + export RBS_TEST_OPT="-Isig -rset -rmutex_m -rforwardable -ruri -rjson -ripaddr -rpathname -rtime -rtimeout -rresolv -rsocket -ropenssl -rbase64 -rzlib -rcgi -rdigest -rhttp-2-next" export RBS_TEST_TARGET="HTTP*" fi