httpx 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/httpx/altsvc.rb +18 -2
- data/lib/httpx/chainable.rb +5 -3
- data/lib/httpx/connection.rb +168 -66
- data/lib/httpx/connection/http1.rb +28 -10
- data/lib/httpx/connection/http2.rb +55 -32
- data/lib/httpx/extensions.rb +1 -1
- data/lib/httpx/io/ssl.rb +11 -3
- data/lib/httpx/io/tcp.rb +12 -2
- data/lib/httpx/loggable.rb +6 -6
- data/lib/httpx/options.rb +17 -14
- data/lib/httpx/plugins/compression.rb +5 -1
- data/lib/httpx/plugins/compression/gzip.rb +22 -12
- data/lib/httpx/plugins/digest_authentication.rb +2 -0
- data/lib/httpx/plugins/proxy.rb +16 -12
- data/lib/httpx/plugins/proxy/http.rb +7 -2
- data/lib/httpx/plugins/proxy/socks4.rb +1 -1
- data/lib/httpx/plugins/proxy/socks5.rb +5 -1
- data/lib/httpx/plugins/push_promise.rb +2 -2
- data/lib/httpx/plugins/retries.rb +11 -5
- data/lib/httpx/pool.rb +7 -12
- data/lib/httpx/registry.rb +2 -1
- data/lib/httpx/request.rb +7 -0
- data/lib/httpx/resolver/https.rb +15 -3
- data/lib/httpx/resolver/native.rb +22 -32
- data/lib/httpx/resolver/options.rb +2 -2
- data/lib/httpx/resolver/resolver_mixin.rb +1 -1
- data/lib/httpx/response.rb +15 -1
- data/lib/httpx/selector.rb +96 -95
- data/lib/httpx/session.rb +1 -1
- data/lib/httpx/timeout.rb +7 -1
- data/lib/httpx/version.rb +1 -1
- metadata +16 -16
data/lib/httpx/response.rb
CHANGED
@@ -58,7 +58,7 @@ module HTTPX
|
|
58
58
|
"HTTP/#{version} " \
|
59
59
|
"@status=#{@status} " \
|
60
60
|
"@headers=#{@headers} " \
|
61
|
-
"@body=#{@body}>"
|
61
|
+
"@body=#{@body.bytesize}>"
|
62
62
|
end
|
63
63
|
# :nocov:
|
64
64
|
|
@@ -150,6 +150,8 @@ module HTTPX
|
|
150
150
|
def copy_to(dest)
|
151
151
|
return unless @buffer
|
152
152
|
|
153
|
+
rewind
|
154
|
+
|
153
155
|
if dest.respond_to?(:path) && @buffer.respond_to?(:path)
|
154
156
|
FileUtils.mv(@buffer.path, dest.path)
|
155
157
|
else
|
@@ -267,6 +269,18 @@ module HTTPX
|
|
267
269
|
@error.message
|
268
270
|
end
|
269
271
|
|
272
|
+
def reason
|
273
|
+
@error.class.name
|
274
|
+
end
|
275
|
+
|
276
|
+
def headers
|
277
|
+
{}
|
278
|
+
end
|
279
|
+
|
280
|
+
def body
|
281
|
+
@error.backtrace.join("\n")
|
282
|
+
end
|
283
|
+
|
270
284
|
def raise_for_status
|
271
285
|
raise @error
|
272
286
|
end
|
data/lib/httpx/selector.rb
CHANGED
@@ -1,5 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "io/wait"
|
4
|
+
|
5
|
+
module IOExtensions # :nodoc:
|
6
|
+
refine IO do
|
7
|
+
def wait(timeout = nil, mode = :read)
|
8
|
+
case mode
|
9
|
+
when :read
|
10
|
+
wait_readable(timeout)
|
11
|
+
when :write
|
12
|
+
wait_writable(timeout)
|
13
|
+
when :read_write
|
14
|
+
r, w = IO.select([self], [self], nil, timeout)
|
15
|
+
|
16
|
+
return unless r || w
|
17
|
+
|
18
|
+
self
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
3
24
|
class HTTPX::Selector
|
4
25
|
READABLE = %i[rw r].freeze
|
5
26
|
WRITABLE = %i[rw w].freeze
|
@@ -7,129 +28,109 @@ class HTTPX::Selector
|
|
7
28
|
private_constant :READABLE
|
8
29
|
private_constant :WRITABLE
|
9
30
|
|
10
|
-
|
11
|
-
# I/O monitor
|
12
|
-
#
|
13
|
-
class Monitor
|
14
|
-
attr_accessor :io, :interests, :readiness
|
15
|
-
|
16
|
-
def initialize(io, interests, reactor)
|
17
|
-
@io = io
|
18
|
-
@interests = interests
|
19
|
-
@reactor = reactor
|
20
|
-
@closed = false
|
21
|
-
end
|
22
|
-
|
23
|
-
def readable?
|
24
|
-
READABLE.include?(@interests)
|
25
|
-
end
|
26
|
-
|
27
|
-
def writable?
|
28
|
-
WRITABLE.include?(@interests)
|
29
|
-
end
|
30
|
-
|
31
|
-
# closes +@io+, deregisters from reactor (unless +deregister+ is false)
|
32
|
-
def close(deregister = true)
|
33
|
-
return if @closed
|
34
|
-
|
35
|
-
@closed = true
|
36
|
-
@reactor.deregister(@io) if deregister
|
37
|
-
end
|
38
|
-
|
39
|
-
def closed?
|
40
|
-
@closed
|
41
|
-
end
|
42
|
-
|
43
|
-
# :nocov:
|
44
|
-
def to_s
|
45
|
-
"#<#{self.class}: #{@io}(closed:#{@closed}) #{@interests} #{object_id.to_s(16)}>"
|
46
|
-
end
|
47
|
-
# :nocov:
|
48
|
-
end
|
31
|
+
using IOExtensions unless IO.method_defined?(:wait) && IO.instance_method(:wait).arity == 2
|
49
32
|
|
50
33
|
def initialize
|
51
|
-
@selectables =
|
52
|
-
@__r__, @__w__ = IO.pipe
|
53
|
-
@closed = false
|
34
|
+
@selectables = []
|
54
35
|
end
|
55
36
|
|
56
37
|
# deregisters +io+ from selectables.
|
57
38
|
def deregister(io)
|
58
|
-
|
59
|
-
monitor.close(false) if monitor
|
39
|
+
@selectables.delete(io)
|
60
40
|
end
|
61
41
|
|
62
|
-
# register +io
|
63
|
-
def register(io
|
64
|
-
|
65
|
-
if monitor
|
66
|
-
monitor.interests = interests
|
67
|
-
else
|
68
|
-
monitor = Monitor.new(io, interests, self)
|
69
|
-
@selectables[io] = monitor
|
70
|
-
end
|
71
|
-
monitor
|
72
|
-
end
|
42
|
+
# register +io+.
|
43
|
+
def register(io)
|
44
|
+
return if @selectables.include?(io)
|
73
45
|
|
74
|
-
|
75
|
-
|
76
|
-
#
|
77
|
-
def select(interval)
|
78
|
-
begin
|
79
|
-
r = [@__r__]
|
80
|
-
w = []
|
46
|
+
@selectables << io
|
47
|
+
end
|
81
48
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
49
|
+
private
|
50
|
+
|
51
|
+
READ_INTERESTS = %i[r rw].freeze
|
52
|
+
WRITE_INTERESTS = %i[w rw].freeze
|
53
|
+
|
54
|
+
def select_many(interval)
|
55
|
+
selectables, r, w = nil
|
56
|
+
|
57
|
+
# first, we group IOs based on interest type. On call to #interests however,
|
58
|
+
# things might already happen, and new IOs might be registered, so we might
|
59
|
+
# have to start all over again. We do this until we group all selectables
|
60
|
+
loop do
|
61
|
+
begin
|
62
|
+
r = nil
|
63
|
+
w = nil
|
64
|
+
|
65
|
+
selectables = @selectables
|
66
|
+
@selectables = []
|
67
|
+
|
68
|
+
selectables.each do |io|
|
69
|
+
interests = io.interests
|
70
|
+
|
71
|
+
(r ||= []) << io if READ_INTERESTS.include?(interests)
|
72
|
+
(w ||= []) << io if WRITE_INTERESTS.include?(interests)
|
73
|
+
end
|
74
|
+
|
75
|
+
if @selectables.empty?
|
76
|
+
@selectables = selectables
|
77
|
+
break
|
78
|
+
else
|
79
|
+
@selectables = [*selectables, @selectables]
|
80
|
+
end
|
81
|
+
rescue StandardError
|
82
|
+
@selectables = selectables if selectables
|
83
|
+
raise
|
86
84
|
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# TODO: what to do if there are no selectables?
|
87
88
|
|
89
|
+
begin
|
88
90
|
readers, writers = IO.select(r, w, nil, interval)
|
89
91
|
|
90
92
|
raise HTTPX::TimeoutError.new(interval, "timed out while waiting on select") if readers.nil? && writers.nil?
|
91
93
|
rescue IOError, SystemCallError
|
92
|
-
@selectables.reject!
|
94
|
+
@selectables.reject!(&:closed?)
|
93
95
|
retry
|
94
96
|
end
|
95
97
|
|
96
98
|
readers.each do |io|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
monitor = io.closed? ? @selectables.delete(io) : @selectables[io]
|
102
|
-
next unless monitor
|
103
|
-
|
104
|
-
monitor.readiness = writers.delete(io) ? :rw : :r
|
105
|
-
yield monitor
|
106
|
-
end
|
99
|
+
yield io
|
100
|
+
|
101
|
+
# so that we don't yield 2 times
|
102
|
+
writers.delete(io)
|
107
103
|
end if readers
|
108
104
|
|
109
105
|
writers.each do |io|
|
110
|
-
|
111
|
-
next unless monitor
|
112
|
-
|
113
|
-
# don't double run this, the last iteration might have run this task already
|
114
|
-
monitor.readiness = :w
|
115
|
-
yield monitor
|
106
|
+
yield io
|
116
107
|
end if writers
|
117
108
|
end
|
118
109
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
110
|
+
def select_one(interval)
|
111
|
+
io = @selectables.first
|
112
|
+
|
113
|
+
interests = io.interests
|
114
|
+
|
115
|
+
result = case interests
|
116
|
+
when :r then io.to_io.wait_readable(interval)
|
117
|
+
when :w then io.to_io.wait_writable(interval)
|
118
|
+
when :rw then io.to_io.wait(interval, :read_write)
|
119
|
+
when nil then return
|
120
|
+
end
|
121
|
+
|
122
|
+
raise HTTPX::TimeoutError.new(interval, "timed out while waiting on select") unless result
|
123
123
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
ensure
|
128
|
-
@closed = true
|
124
|
+
yield io
|
125
|
+
rescue IOError, SystemCallError
|
126
|
+
@selectables.reject!(&:closed?)
|
129
127
|
end
|
130
128
|
|
131
|
-
|
132
|
-
|
133
|
-
|
129
|
+
def select(interval, &block)
|
130
|
+
return select_one(interval, &block) if @selectables.size == 1
|
131
|
+
|
132
|
+
select_many(interval, &block)
|
134
133
|
end
|
134
|
+
|
135
|
+
public :select
|
135
136
|
end
|
data/lib/httpx/session.rb
CHANGED
data/lib/httpx/timeout.rb
CHANGED
@@ -6,6 +6,7 @@ module HTTPX
|
|
6
6
|
class Timeout
|
7
7
|
CONNECT_TIMEOUT = 60
|
8
8
|
OPERATION_TIMEOUT = 60
|
9
|
+
KEEP_ALIVE_TIMEOUT = 20
|
9
10
|
|
10
11
|
def self.new(opts = {})
|
11
12
|
return opts if opts.is_a?(Timeout)
|
@@ -13,14 +14,16 @@ module HTTPX
|
|
13
14
|
super(**opts)
|
14
15
|
end
|
15
16
|
|
16
|
-
attr_reader :connect_timeout, :operation_timeout, :total_timeout
|
17
|
+
attr_reader :connect_timeout, :operation_timeout, :keep_alive_timeout, :total_timeout
|
17
18
|
|
18
19
|
def initialize(connect_timeout: CONNECT_TIMEOUT,
|
19
20
|
operation_timeout: OPERATION_TIMEOUT,
|
21
|
+
keep_alive_timeout: KEEP_ALIVE_TIMEOUT,
|
20
22
|
total_timeout: nil,
|
21
23
|
loop_timeout: nil)
|
22
24
|
@connect_timeout = connect_timeout
|
23
25
|
@operation_timeout = operation_timeout
|
26
|
+
@keep_alive_timeout = keep_alive_timeout
|
24
27
|
@total_timeout = total_timeout
|
25
28
|
|
26
29
|
return unless loop_timeout
|
@@ -35,6 +38,7 @@ module HTTPX
|
|
35
38
|
if other.is_a?(Timeout)
|
36
39
|
@connect_timeout == other.instance_variable_get(:@connect_timeout) &&
|
37
40
|
@operation_timeout == other.instance_variable_get(:@operation_timeout) &&
|
41
|
+
@keep_alive_timeout == other.instance_variable_get(:@keep_alive_timeout) &&
|
38
42
|
@total_timeout == other.instance_variable_get(:@total_timeout)
|
39
43
|
else
|
40
44
|
super
|
@@ -49,9 +53,11 @@ module HTTPX
|
|
49
53
|
when Timeout
|
50
54
|
connect_timeout = other.instance_variable_get(:@connect_timeout) || @connect_timeout
|
51
55
|
operation_timeout = other.instance_variable_get(:@operation_timeout) || @operation_timeout
|
56
|
+
keep_alive_timeout = other.instance_variable_get(:@keep_alive_timeout) || @keep_alive_timeout
|
52
57
|
total_timeout = other.instance_variable_get(:@total_timeout) || @total_timeout
|
53
58
|
Timeout.new(connect_timeout: connect_timeout,
|
54
59
|
operation_timeout: operation_timeout,
|
60
|
+
keep_alive_timeout: keep_alive_timeout,
|
55
61
|
total_timeout: total_timeout)
|
56
62
|
else
|
57
63
|
raise ArgumentError, "can't merge with #{other.class}"
|
data/lib/httpx/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: httpx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tiago Cardoso
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-04-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: http-2-next
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: http-cookie
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: http-form_data
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,20 +72,6 @@ dependencies:
|
|
58
72
|
- - "<"
|
59
73
|
- !ruby/object:Gem::Version
|
60
74
|
version: '3'
|
61
|
-
- !ruby/object:Gem::Dependency
|
62
|
-
name: http-cookie
|
63
|
-
requirement: !ruby/object:Gem::Requirement
|
64
|
-
requirements:
|
65
|
-
- - "~>"
|
66
|
-
- !ruby/object:Gem::Version
|
67
|
-
version: '1.0'
|
68
|
-
type: :development
|
69
|
-
prerelease: false
|
70
|
-
version_requirements: !ruby/object:Gem::Requirement
|
71
|
-
requirements:
|
72
|
-
- - "~>"
|
73
|
-
- !ruby/object:Gem::Version
|
74
|
-
version: '1.0'
|
75
75
|
description: A client library for making HTTP requests from Ruby.
|
76
76
|
email:
|
77
77
|
- cardoso_tiago@hotmail.com
|