httpray 1.1.1 → 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +2 -3
- data/README.md +8 -2
- data/lib/httpray.rb +22 -10
- data/lib/httpray/version.rb +1 -1
- data/test/httpray_test.rb +15 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 85f7ee216d286ddb7923f073086321aaad64ed36
|
4
|
+
data.tar.gz: c8a0b734b3565a1bf8a9391ce0bee417c1e015c5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d1f1f90d6d72dceeba9ed80b7ba01247e0043ee5933737d0f9cc17aa9f24f15393ebdd882be7c13d2afee162256eb319b0f4f562e16486b9c659ff524c276082
|
7
|
+
data.tar.gz: b84071e158ce7b043b56b77573159cd954a43983f9dfc94b56385b7acd9780303e8bca198e4c13ff281dca047dd7a6af7f1d69f834755b089dc9d4675d2b2ef7
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -43,7 +43,13 @@ socket.close
|
|
43
43
|
# use a persistent connection to keep the party rolling
|
44
44
|
# will automatically reconnect if connection is lost
|
45
45
|
uri = URI.parse("https://your.diety/pray")
|
46
|
-
ark = HTTPray::Connection.new(
|
46
|
+
ark = HTTPray::Connection.new(
|
47
|
+
uri.host,
|
48
|
+
uri.port,
|
49
|
+
1, #timeout
|
50
|
+
OpenSSL::SSL::SSLContext.new, #ssl context
|
51
|
+
1, #retries on error
|
52
|
+
10) #seconds to wait after all retries failed before trying again on a subsequent request
|
47
53
|
ark.request("POST", uri,
|
48
54
|
{"Content-Type" => "application/prayer"},
|
49
55
|
"Why did it have to be snakes?")
|
@@ -58,7 +64,7 @@ ark.request("POST", uri,
|
|
58
64
|
|
59
65
|
HTTPray has minimal convenience and sanitization features because I didn't need them. All that it does is fill in the Host, User-Agent, Accept, and Content-Length headers for you. The body must be a string, so convert it yourself first. The URI can be a `URI` or a `String` that will go through `URI.parse`. You're welcome.
|
60
66
|
|
61
|
-
If you're using `Connection` with TLS you need to provide an `OpenSSL::SSL::SSLContext` when creating the connection. `HTTPray.request!` is more forgiving and if you don't give it one it will create it for you if needed.
|
67
|
+
If you're using `Connection` with TLS you need to provide an `OpenSSL::SSL::SSLContext` when creating the connection. `HTTPray.request!` is more forgiving and if you don't give it one it will create it for you if needed. `Connection` also has some fancy retry and circuit breaker logic so you can assume your request always writes even if it doesn't without major performance impacts.
|
62
68
|
|
63
69
|
Timeout support does not extend to the response since you just get back a `Socket`. You're own your own for how you want to handle that.
|
64
70
|
|
data/lib/httpray.rb
CHANGED
@@ -6,6 +6,7 @@ require_relative 'httpray/version'
|
|
6
6
|
|
7
7
|
module HTTPray
|
8
8
|
class Timeout < StandardError; end
|
9
|
+
class CircuitBreakerError < StandardError; end
|
9
10
|
|
10
11
|
DEFAULT_HEADERS = {
|
11
12
|
"User-Agent" => "HTTPray #{VERSION}",
|
@@ -14,12 +15,14 @@ module HTTPray
|
|
14
15
|
}.freeze
|
15
16
|
|
16
17
|
class Connection
|
17
|
-
def initialize(host, port, timeout = 1, ssl_context = nil, retry_count = 1)
|
18
|
+
def initialize(host, port, timeout = 1, ssl_context = nil, retry_count = 1, retry_circuit_breaker_interval = 10)
|
18
19
|
@host = host
|
19
20
|
@port = port
|
20
21
|
@timeout = timeout
|
21
22
|
@ssl_context = ssl_context
|
22
23
|
@retry_count = retry_count
|
24
|
+
@retry_circuit_breaker_interval = retry_circuit_breaker_interval
|
25
|
+
@next_retry_time = Time.now - @retry_circuit_breaker_interval
|
23
26
|
@socket = connect
|
24
27
|
end
|
25
28
|
|
@@ -29,12 +32,13 @@ module HTTPray
|
|
29
32
|
@socket
|
30
33
|
end
|
31
34
|
|
35
|
+
def reconnect
|
36
|
+
@socket = connect unless @socket && !socket.closed?
|
37
|
+
end
|
38
|
+
|
32
39
|
def request!(method, uri, headers = {}, body = nil)
|
33
40
|
tries ||= 0
|
34
|
-
|
35
|
-
IO.select([@socket], [@socket], [@socket], @timeout) if @socket
|
36
|
-
rescue; end
|
37
|
-
@socket = connect unless @socket && !@socket.closed?
|
41
|
+
reconnect
|
38
42
|
socket = @socket
|
39
43
|
uri = URI.parse(uri) unless URI === uri
|
40
44
|
|
@@ -48,11 +52,19 @@ module HTTPray
|
|
48
52
|
socket.write_nonblock "\r\n"
|
49
53
|
socket.write_nonblock body if body
|
50
54
|
socket
|
51
|
-
rescue
|
52
|
-
@socket.close
|
53
|
-
|
54
|
-
|
55
|
-
|
55
|
+
rescue => error
|
56
|
+
@socket.close unless @socket.closed?
|
57
|
+
now = Time.now
|
58
|
+
if now > @next_retry_time
|
59
|
+
if tries < @retry_count
|
60
|
+
tries += 1
|
61
|
+
retry
|
62
|
+
else
|
63
|
+
@next_retry_time = now + @retry_circuit_breaker_interval
|
64
|
+
raise error
|
65
|
+
end
|
66
|
+
else
|
67
|
+
raise HTTPray::CircuitBreakerError
|
56
68
|
end
|
57
69
|
end
|
58
70
|
|
data/lib/httpray/version.rb
CHANGED
data/test/httpray_test.rb
CHANGED
@@ -106,4 +106,19 @@ class HTTPrayTest < MiniTest::Test
|
|
106
106
|
ark.request("GET", uri, {"Connection" => ""})
|
107
107
|
end
|
108
108
|
end
|
109
|
+
def test_retries_stopped_by_circuit_breaker
|
110
|
+
uri = URI.parse("http://httpbin.org/deny")
|
111
|
+
ark = HTTPray::Connection.new(uri.host, uri.port, 1, nil, 0, 2)
|
112
|
+
# force retries to expire
|
113
|
+
ark.socket.stub(:write_nonblock, lambda { |*args| raise "Broken pipe" }) do
|
114
|
+
assert_raises RuntimeError do; ark.request("GET", uri, {"Connection" => ""}); end
|
115
|
+
end
|
116
|
+
ark.stub(:reconnect, ark.socket) do #prevent reconnect of closed socket from previous error
|
117
|
+
ark.socket.stub(:write_nonblock, lambda { |*args| raise "Broken pipe" }) do
|
118
|
+
assert_raises HTTPray::CircuitBreakerError do; ark.request("GET", uri, {"Connection" => ""}); end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
sleep(2)
|
122
|
+
ark.request("GET", uri, {"Connection" => ""})
|
123
|
+
end
|
109
124
|
end
|