-
Notifications
You must be signed in to change notification settings - Fork 80
/
net_http.rb
154 lines (124 loc) · 3.98 KB
/
net_http.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# frozen_string_literal: true
require "semian/adapter"
require "net/http"
module Net
ProtocolError.include(::Semian::AdapterError)
class SemianError < ::Net::ProtocolError
def initialize(semian_identifier, *args)
super(*args)
@semian_identifier = semian_identifier
end
end
ResourceBusyError = Class.new(SemianError)
CircuitOpenError = Class.new(SemianError)
end
module Semian
module NetHTTP
include Semian::Adapter
ResourceBusyError = ::Net::ResourceBusyError
CircuitOpenError = ::Net::CircuitOpenError
class SemianConfigurationChangedError < RuntimeError
def initialize(msg = "Cannot re-initialize semian_configuration")
super
end
end
def semian_identifier
"nethttp_#{raw_semian_options[:name]}"
end
DEFAULT_ERRORS = [
::Timeout::Error, # includes ::Net::ReadTimeout and ::Net::OpenTimeout
::SocketError,
::Net::HTTPBadResponse,
::Net::HTTPHeaderSyntaxError,
::Net::ProtocolError,
::EOFError,
::IOError,
::SystemCallError, # includes ::Errno::EINVAL, ::Errno::ECONNRESET,
# ::Errno::ECONNREFUSED, ::Errno::ETIMEDOUT, and more
].freeze # Net::HTTP can throw many different errors, this tries to capture most of them
module ClassMethods
def new(*args, semian: true)
http = super(*args)
http.instance_variable_set(:@semian_enabled, semian)
http
end
end
class << self
attr_accessor :exceptions
attr_reader :semian_configuration
def semian_configuration=(configuration)
raise Semian::NetHTTP::SemianConfigurationChangedError unless @semian_configuration.nil?
@semian_configuration = configuration
end
def retrieve_semian_configuration(host, port)
@semian_configuration.call(host, port) if @semian_configuration.respond_to?(:call)
end
def reset_exceptions
self.exceptions = Semian::NetHTTP::DEFAULT_ERRORS.dup
end
end
Semian::NetHTTP.reset_exceptions
def raw_semian_options
@raw_semian_options ||= begin
@raw_semian_options = Semian::NetHTTP.retrieve_semian_configuration(address, port)
@raw_semian_options = @raw_semian_options.dup unless @raw_semian_options.nil?
end
end
def resource_exceptions
Semian::NetHTTP.exceptions
end
def disabled?
raw_semian_options.nil? || @semian_enabled == false
end
def connect
with_cleared_dynamic_options do
return super if disabled?
acquire_semian_resource(adapter: :http, scope: :connection) { super }
end
end
def transport_request(*)
with_cleared_dynamic_options do
return super if disabled?
acquire_semian_resource(adapter: :http, scope: :query) do
handle_error_responses(super)
end
end
end
def with_resource_timeout(timeout)
prev_read_timeout = read_timeout
prev_open_timeout = open_timeout
begin
self.read_timeout = timeout
self.open_timeout = timeout
yield
ensure
self.read_timeout = prev_read_timeout
self.open_timeout = prev_open_timeout
end
end
private
def handle_error_responses(result)
if raw_semian_options.fetch(:open_circuit_server_errors, false)
semian_resource.mark_failed(result) if result.is_a?(::Net::HTTPServerError)
end
result
end
def with_cleared_dynamic_options
unless @resource_acquisition_in_progress
@resource_acquisition_in_progress = true
resource_acquisition_started = true
end
yield
ensure
if resource_acquisition_started
if @raw_semian_options&.fetch(:dynamic, false)
# Clear @raw_semian_options if the resource was flagged as dynamic.
@raw_semian_options = nil
end
@resource_acquisition_in_progress = false
end
end
end
end
Net::HTTP.prepend(Semian::NetHTTP)
Net::HTTP.singleton_class.prepend(Semian::NetHTTP::ClassMethods)