httpx 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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
- monitor = @selectables.delete(io)
59
- monitor.close(false) if monitor
39
+ @selectables.delete(io)
60
40
  end
61
41
 
62
- # register +io+ for +interests+ events.
63
- def register(io, interests)
64
- monitor = @selectables[io]
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
- # waits for read/write events for +interval+. Yields for monitors of
75
- # selected IO objects.
76
- #
77
- def select(interval)
78
- begin
79
- r = [@__r__]
80
- w = []
46
+ @selectables << io
47
+ end
81
48
 
82
- @selectables.each do |io, monitor|
83
- r << io if monitor.interests == :r || monitor.interests == :rw
84
- w << io if monitor.interests == :w || monitor.interests == :rw
85
- monitor.readiness = nil
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! { |io, _| io.closed? }
94
+ @selectables.reject!(&:closed?)
93
95
  retry
94
96
  end
95
97
 
96
98
  readers.each do |io|
97
- if io == @__r__
98
- # clean up wakeups
99
- @__r__.read(@__r__.stat.size)
100
- else
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
- monitor = io.closed? ? @selectables.delete(io) : @selectables[io]
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
- # Closes the selector.
120
- #
121
- def close
122
- return if @closed
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
- @__r__.close
125
- @__w__.close
126
- rescue IOError
127
- ensure
128
- @closed = true
124
+ yield io
125
+ rescue IOError, SystemCallError
126
+ @selectables.reject!(&:closed?)
129
127
  end
130
128
 
131
- # interrupts the select call.
132
- def wakeup
133
- @__w__.write_nonblock("\0", exception: false)
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
@@ -47,7 +47,7 @@ module HTTPX
47
47
  end
48
48
 
49
49
  def on_promise(_, stream)
50
- log(level: 2, label: "#{stream.id}: ") { "refusing stream!" }
50
+ log(level: 2) { "#{stream.id}: refusing stream!" }
51
51
  stream.refuse
52
52
  end
53
53
 
@@ -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}"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTPX
4
- VERSION = "0.7.0"
4
+ VERSION = "0.8.0"
5
5
  end
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.7.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-03-15 00:00:00.000000000 Z
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