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
|
# frozen_string_literal: true
module Net
module Hippie
# A simple client for connecting with http resources.
class Client
DEFAULT_HEADERS = {
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'User-Agent' => "net/hippie #{Net::Hippie::VERSION}"
}.freeze
attr_accessor :mapper
attr_accessor :read_timeout
attr_accessor :logger
def initialize(
certificate: nil,
headers: DEFAULT_HEADERS,
key: nil,
passphrase: nil,
verify_mode: nil
)
@certificate = certificate
@default_headers = headers
@key = key
@mapper = ContentTypeMapper.new
@passphrase = passphrase
@read_timeout = 30
@verify_mode = verify_mode
@logger = Net::Hippie.logger
end
def execute(uri, request)
response = http_for(normalize_uri(uri)).request(request)
if block_given?
yield request, response
else
response
end
end
def get(uri, headers: {}, body: {}, &block)
request = request_for(Net::HTTP::Get, uri, headers: headers, body: body)
execute(uri, request, &block)
end
def post(uri, headers: {}, body: {}, &block)
type = Net::HTTP::Post
request = request_for(type, uri, headers: headers, body: body)
execute(uri, request, &block)
end
def put(uri, headers: {}, body: {}, &block)
request = request_for(Net::HTTP::Put, uri, headers: headers, body: body)
execute(uri, request, &block)
end
def delete(uri, headers: {}, body: {}, &block)
type = Net::HTTP::Delete
request = request_for(type, uri, headers: headers, body: body)
execute(uri, request, &block)
end
# attempt 1 -> delay 1 second
# attempt 2 -> delay 2 second
# attempt 3 -> delay 4 second
# attempt 4 -> delay 8 second
# attempt 5 -> delay 16 second
# attempt 6 -> delay 32 second
# attempt 7 -> delay 64 second
# attempt 8 -> delay 128 second
def with_retry(retries: 3)
retries = 3 if retries <= 0
0.upto(retries) do |n|
return yield self
rescue *::Net::Hippie::CONNECTION_ERRORS => error
raise error if n >= retries
jitter = rand(0.5)
delay = (2**n) + jitter
logger.warn("Retry: #{n + 1}/#{retries}. Delay: #{delay} second(s)")
logger.warn(error.message)
sleep delay
end
end
private
attr_reader :default_headers
attr_reader :verify_mode
attr_reader :certificate, :key, :passphrase
def http_for(uri)
http = Net::HTTP.new(uri.host, uri.port)
http.read_timeout = read_timeout
http.use_ssl = uri.scheme == 'https'
http.verify_mode = verify_mode
http.set_debug_output(logger)
apply_client_tls_to(http)
http
end
def request_for(type, uri, headers: {}, body: {})
final_headers = default_headers.merge(headers)
type.new(uri.to_s, final_headers).tap do |x|
x.body = mapper.map_from(final_headers, body) unless body.empty?
end
end
def normalize_uri(uri)
uri.is_a?(URI) ? uri : URI.parse(uri)
end
def private_key(type = OpenSSL::PKey::RSA)
passphrase ? type.new(key, passphrase) : type.new(key)
end
def apply_client_tls_to(http)
return if certificate.nil? || key.nil?
http.cert = OpenSSL::X509::Certificate.new(certificate)
http.key = private_key
end
end
end
end
|