httpx 0.13.0 → 0.14.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/doc/release_notes/0_10_1.md +1 -1
  3. data/doc/release_notes/0_13_0.md +2 -2
  4. data/doc/release_notes/0_13_1.md +5 -0
  5. data/doc/release_notes/0_13_2.md +9 -0
  6. data/doc/release_notes/0_14_0.md +79 -0
  7. data/doc/release_notes/0_14_1.md +7 -0
  8. data/doc/release_notes/0_14_2.md +6 -0
  9. data/lib/httpx.rb +1 -2
  10. data/lib/httpx/callbacks.rb +12 -3
  11. data/lib/httpx/connection.rb +12 -9
  12. data/lib/httpx/connection/http1.rb +32 -14
  13. data/lib/httpx/connection/http2.rb +61 -15
  14. data/lib/httpx/headers.rb +7 -3
  15. data/lib/httpx/io/tcp.rb +3 -1
  16. data/lib/httpx/io/udp.rb +31 -7
  17. data/lib/httpx/options.rb +91 -56
  18. data/lib/httpx/plugins/aws_sdk_authentication.rb +5 -2
  19. data/lib/httpx/plugins/aws_sigv4.rb +5 -4
  20. data/lib/httpx/plugins/basic_authentication.rb +8 -3
  21. data/lib/httpx/plugins/compression.rb +8 -8
  22. data/lib/httpx/plugins/compression/brotli.rb +4 -3
  23. data/lib/httpx/plugins/compression/deflate.rb +4 -3
  24. data/lib/httpx/plugins/compression/gzip.rb +2 -1
  25. data/lib/httpx/plugins/cookies.rb +3 -7
  26. data/lib/httpx/plugins/digest_authentication.rb +4 -4
  27. data/lib/httpx/plugins/expect.rb +6 -6
  28. data/lib/httpx/plugins/follow_redirects.rb +3 -3
  29. data/lib/httpx/plugins/grpc.rb +247 -0
  30. data/lib/httpx/plugins/grpc/call.rb +62 -0
  31. data/lib/httpx/plugins/grpc/message.rb +85 -0
  32. data/lib/httpx/plugins/multipart/part.rb +2 -2
  33. data/lib/httpx/plugins/proxy.rb +3 -7
  34. data/lib/httpx/plugins/proxy/http.rb +5 -4
  35. data/lib/httpx/plugins/proxy/ssh.rb +3 -3
  36. data/lib/httpx/plugins/rate_limiter.rb +1 -1
  37. data/lib/httpx/plugins/retries.rb +13 -14
  38. data/lib/httpx/plugins/stream.rb +96 -74
  39. data/lib/httpx/plugins/upgrade.rb +6 -5
  40. data/lib/httpx/request.rb +25 -2
  41. data/lib/httpx/resolver/native.rb +7 -3
  42. data/lib/httpx/response.rb +4 -0
  43. data/lib/httpx/session.rb +17 -7
  44. data/lib/httpx/transcoder/chunker.rb +1 -1
  45. data/lib/httpx/version.rb +1 -1
  46. data/sig/callbacks.rbs +2 -0
  47. data/sig/connection/http1.rbs +5 -1
  48. data/sig/connection/http2.rbs +6 -2
  49. data/sig/headers.rbs +2 -2
  50. data/sig/options.rbs +9 -2
  51. data/sig/plugins/aws_sdk_authentication.rbs +2 -0
  52. data/sig/plugins/basic_authentication.rbs +2 -0
  53. data/sig/plugins/compression.rbs +2 -2
  54. data/sig/plugins/multipart.rbs +1 -1
  55. data/sig/plugins/stream.rbs +17 -16
  56. data/sig/request.rbs +7 -2
  57. data/sig/response.rbs +1 -0
  58. data/sig/session.rbs +4 -0
  59. metadata +18 -7
  60. data/lib/httpx/timeout.rb +0 -67
  61. data/sig/timeout.rbs +0 -29
data/lib/httpx/headers.rb CHANGED
@@ -103,16 +103,20 @@ module HTTPX
103
103
  # returns the enumerable headers store in pairs of header field + the values in
104
104
  # the comma-separated string format
105
105
  #
106
- def each
107
- return enum_for(__method__) { @headers.size } unless block_given?
106
+ def each(extra_headers = nil)
107
+ return enum_for(__method__, extra_headers) { @headers.size } unless block_given?
108
108
 
109
109
  @headers.each do |field, value|
110
110
  yield(field, value.join(", ")) unless value.empty?
111
111
  end
112
+
113
+ extra_headers.each do |field, value|
114
+ yield(field, value) unless value.empty?
115
+ end if extra_headers
112
116
  end
113
117
 
114
118
  def ==(other)
115
- to_hash == Headers.new(other).to_hash
119
+ other == to_hash
116
120
  end
117
121
 
118
122
  # the headers store in Hash format
data/lib/httpx/io/tcp.rb CHANGED
@@ -63,7 +63,7 @@ module HTTPX
63
63
  @ip_index -= 1
64
64
  retry
65
65
  rescue Errno::ETIMEDOUT => e
66
- raise ConnectTimeoutError.new(@options.timeout.connect_timeout, e.message) if @ip_index <= 0
66
+ raise ConnectTimeoutError.new(@options.timeout[:connect_timeout], e.message) if @ip_index <= 0
67
67
 
68
68
  @ip_index -= 1
69
69
  retry
@@ -120,6 +120,8 @@ module HTTPX
120
120
  end
121
121
  transition(:connected)
122
122
  @interests = :w
123
+ rescue Errno::EALREADY
124
+ @interests = :w
123
125
  end
124
126
  private :try_connect
125
127
 
data/lib/httpx/io/udp.rb CHANGED
@@ -39,16 +39,20 @@ module HTTPX
39
39
  end
40
40
  end
41
41
 
42
- def write(buffer)
43
- siz = @io.send(buffer.to_s, 0, @host, @port)
44
- log { "WRITE: #{siz} bytes..." }
45
- buffer.shift!(siz)
46
- siz
47
- end
48
-
49
42
  # :nocov:
50
43
  if (RUBY_ENGINE == "truffleruby" && RUBY_ENGINE_VERSION < "21.1.0") ||
51
44
  RUBY_VERSION < "2.3"
45
+ def write(buffer)
46
+ siz = @io.sendmsg_nonblock(buffer.to_s, 0, Socket.sockaddr_in(@port, @host.to_s))
47
+ log { "WRITE: #{siz} bytes..." }
48
+ buffer.shift!(siz)
49
+ siz
50
+ rescue ::IO::WaitWritable
51
+ 0
52
+ rescue EOFError
53
+ nil
54
+ end
55
+
52
56
  def read(size, buffer)
53
57
  data, _ = @io.recvfrom_nonblock(size)
54
58
  buffer.replace(data)
@@ -59,6 +63,18 @@ module HTTPX
59
63
  rescue IOError
60
64
  end
61
65
  else
66
+
67
+ def write(buffer)
68
+ siz = @io.sendmsg_nonblock(buffer.to_s, 0, Socket.sockaddr_in(@port, @host.to_s), exception: false)
69
+ return 0 if siz == :wait_writable
70
+ return if siz.nil?
71
+
72
+ log { "WRITE: #{siz} bytes..." }
73
+
74
+ buffer.shift!(siz)
75
+ siz
76
+ end
77
+
62
78
  def read(size, buffer)
63
79
  ret = @io.recvfrom_nonblock(size, 0, buffer, exception: false)
64
80
  return 0 if ret == :wait_readable
@@ -68,6 +84,14 @@ module HTTPX
68
84
  rescue IOError
69
85
  end
70
86
  end
87
+
88
+ # In JRuby, sendmsg_nonblock is not implemented
89
+ def write(buffer)
90
+ siz = @io.send(buffer.to_s, 0, @host, @port)
91
+ log { "WRITE: #{siz} bytes..." }
92
+ buffer.shift!(siz)
93
+ siz
94
+ end if RUBY_ENGINE == "jruby"
71
95
  # :nocov:
72
96
  end
73
97
  end
data/lib/httpx/options.rb CHANGED
@@ -4,6 +4,37 @@ module HTTPX
4
4
  class Options
5
5
  WINDOW_SIZE = 1 << 14 # 16K
6
6
  MAX_BODY_THRESHOLD_SIZE = (1 << 10) * 112 # 112K
7
+ CONNECT_TIMEOUT = 60
8
+ OPERATION_TIMEOUT = 60
9
+ KEEP_ALIVE_TIMEOUT = 20
10
+
11
+ DEFAULT_OPTIONS = {
12
+ :debug => ENV.key?("HTTPX_DEBUG") ? $stderr : nil,
13
+ :debug_level => (ENV["HTTPX_DEBUG"] || 1).to_i,
14
+ :ssl => {},
15
+ :http2_settings => { settings_enable_push: 0 },
16
+ :fallback_protocol => "http/1.1",
17
+ :timeout => {
18
+ connect_timeout: CONNECT_TIMEOUT,
19
+ operation_timeout: OPERATION_TIMEOUT,
20
+ keep_alive_timeout: KEEP_ALIVE_TIMEOUT,
21
+ },
22
+ :headers => {},
23
+ :window_size => WINDOW_SIZE,
24
+ :body_threshold_size => MAX_BODY_THRESHOLD_SIZE,
25
+ :request_class => Class.new(Request),
26
+ :response_class => Class.new(Response),
27
+ :headers_class => Class.new(Headers),
28
+ :request_body_class => Class.new(Request::Body),
29
+ :response_body_class => Class.new(Response::Body),
30
+ :connection_class => Class.new(Connection),
31
+ :transport => nil,
32
+ :transport_options => nil,
33
+ :addresses => nil,
34
+ :persistent => false,
35
+ :resolver_class => (ENV["HTTPX_RESOLVER"] || :native).to_sym,
36
+ :resolver_options => { cache: true },
37
+ }.freeze
7
38
 
8
39
  class << self
9
40
  def new(options = {})
@@ -14,10 +45,23 @@ module HTTPX
14
45
  super
15
46
  end
16
47
 
17
- def def_option(name, &interpreter)
48
+ def def_option(name, layout = nil, &interpreter)
18
49
  attr_reader name
19
50
 
20
- if interpreter
51
+ if layout
52
+ class_eval(<<-OUT, __FILE__, __LINE__ + 1)
53
+ def #{name}=(value)
54
+ return if value.nil?
55
+
56
+ value = begin
57
+ #{layout}
58
+ end
59
+
60
+ @#{name} = value
61
+ end
62
+ OUT
63
+
64
+ elsif interpreter
21
65
  define_method(:"#{name}=") do |value|
22
66
  return if value.nil?
23
67
 
@@ -32,80 +76,71 @@ module HTTPX
32
76
  end
33
77
 
34
78
  def initialize(options = {})
35
- defaults = {
36
- :debug => ENV.key?("HTTPX_DEBUG") ? $stderr : nil,
37
- :debug_level => (ENV["HTTPX_DEBUG"] || 1).to_i,
38
- :ssl => {},
39
- :http2_settings => { settings_enable_push: 0 },
40
- :fallback_protocol => "http/1.1",
41
- :timeout => Timeout.new,
42
- :headers => {},
43
- :window_size => WINDOW_SIZE,
44
- :body_threshold_size => MAX_BODY_THRESHOLD_SIZE,
45
- :request_class => Class.new(Request),
46
- :response_class => Class.new(Response),
47
- :headers_class => Class.new(Headers),
48
- :request_body_class => Class.new(Request::Body),
49
- :response_body_class => Class.new(Response::Body),
50
- :connection_class => Class.new(Connection),
51
- :transport => nil,
52
- :transport_options => nil,
53
- :addresses => nil,
54
- :persistent => false,
55
- :resolver_class => (ENV["HTTPX_RESOLVER"] || :native).to_sym,
56
- :resolver_options => { cache: true },
57
- }
58
-
59
- defaults.merge!(options)
79
+ defaults = DEFAULT_OPTIONS.merge(options)
60
80
  defaults.each do |(k, v)|
61
81
  next if v.nil?
62
82
 
63
- __send__(:"#{k}=", v)
83
+ begin
84
+ __send__(:"#{k}=", v)
85
+ rescue NoMethodError
86
+ raise Error, "unknown option: #{k}"
87
+ end
64
88
  end
65
89
  end
66
90
 
67
- def_option(:headers) do |headers|
91
+ def_option(:origin, <<-OUT)
92
+ URI(value)
93
+ OUT
94
+
95
+ def_option(:headers, <<-OUT)
68
96
  if self.headers
69
- self.headers.merge(headers)
97
+ self.headers.merge(value)
70
98
  else
71
- Headers.new(headers)
99
+ Headers.new(value)
72
100
  end
73
- end
101
+ OUT
74
102
 
75
- def_option(:timeout) do |opts|
76
- Timeout.new(opts)
77
- end
103
+ def_option(:timeout, <<-OUT)
104
+ timeouts = Hash[value]
105
+
106
+ if timeouts.key?(:loop_timeout)
107
+ warn ":loop_timeout is deprecated, use :operation_timeout instead"
108
+ timeouts[:operation_timeout] = timeouts.delete(:loop_timeout)
109
+ end
78
110
 
79
- def_option(:max_concurrent_requests) do |num|
80
- raise Error, ":max_concurrent_requests must be positive" unless num.positive?
111
+ timeouts
112
+ OUT
81
113
 
82
- num
83
- end
114
+ def_option(:max_concurrent_requests, <<-OUT)
115
+ raise Error, ":max_concurrent_requests must be positive" unless value.positive?
84
116
 
85
- def_option(:max_requests) do |num|
86
- raise Error, ":max_requests must be positive" unless num.positive?
117
+ value
118
+ OUT
87
119
 
88
- num
89
- end
120
+ def_option(:max_requests, <<-OUT)
121
+ raise Error, ":max_requests must be positive" unless value.positive?
90
122
 
91
- def_option(:window_size) do |num|
92
- Integer(num)
93
- end
123
+ value
124
+ OUT
94
125
 
95
- def_option(:body_threshold_size) do |num|
96
- Integer(num)
97
- end
126
+ def_option(:window_size, <<-OUT)
127
+ Integer(value)
128
+ OUT
129
+
130
+ def_option(:body_threshold_size, <<-OUT)
131
+ Integer(value)
132
+ OUT
98
133
 
99
- def_option(:transport) do |tr|
100
- transport = tr.to_s
101
- raise Error, "#{transport} is an unsupported transport type" unless IO.registry.key?(transport)
134
+ def_option(:transport, <<-OUT)
135
+ transport = value.to_s
136
+ raise Error, "\#{transport} is an unsupported transport type" unless IO.registry.key?(transport)
102
137
 
103
138
  transport
104
- end
139
+ OUT
105
140
 
106
- def_option(:addresses) do |addrs|
107
- Array(addrs)
108
- end
141
+ def_option(:addresses, <<-OUT)
142
+ Array(value)
143
+ OUT
109
144
 
110
145
  %w[
111
146
  params form json body ssl http2_settings
@@ -32,9 +32,8 @@ module HTTPX
32
32
  class << self
33
33
  attr_reader :credentials, :region
34
34
 
35
- def load_dependencies(klass)
35
+ def load_dependencies(_klass)
36
36
  require "aws-sdk-core"
37
- klass.plugin(:aws_sigv4)
38
37
 
39
38
  client = Class.new(Seahorse::Client::Base) do
40
39
  @identifier = :httpx
@@ -50,6 +49,10 @@ module HTTPX
50
49
  @region = client.config[:region]
51
50
  end
52
51
 
52
+ def configure(klass)
53
+ klass.plugin(:aws_sigv4)
54
+ end
55
+
53
56
  def extra_options(options)
54
57
  options.merge(max_concurrent_requests: 1)
55
58
  end
@@ -143,13 +143,14 @@ module HTTPX
143
143
  class << self
144
144
  def extra_options(options)
145
145
  Class.new(options.class) do
146
- def_option(:sigv4_signer) do |signer|
147
- signer.is_a?(Signer) ? signer : Signer.new(signer)
148
- end
149
- end.new.merge(options)
146
+ def_option(:sigv4_signer, <<-OUT)
147
+ value.is_a?(#{Signer}) ? value : #{Signer}.new(value)
148
+ OUT
149
+ end.new(options)
150
150
  end
151
151
 
152
152
  def load_dependencies(klass)
153
+ require "digest/sha2"
153
154
  require "openssl"
154
155
  klass.plugin(:expect)
155
156
  klass.plugin(:compression)
@@ -8,9 +8,14 @@ module HTTPX
8
8
  # https://gitlab.com/honeyryderchuck/httpx/wikis/Authentication#basic-authentication
9
9
  #
10
10
  module BasicAuthentication
11
- def self.load_dependencies(klass)
12
- require "base64"
13
- klass.plugin(:authentication)
11
+ class << self
12
+ def load_dependencies(_klass)
13
+ require "base64"
14
+ end
15
+
16
+ def configure(klass)
17
+ klass.plugin(:authentication)
18
+ end
14
19
  end
15
20
 
16
21
  module InstanceMethods
@@ -25,18 +25,18 @@ module HTTPX
25
25
  end
26
26
 
27
27
  Class.new(options.class) do
28
- def_option(:compression_threshold_size) do |bytes|
29
- bytes = Integer(bytes)
28
+ def_option(:compression_threshold_size, <<-OUT)
29
+ bytes = Integer(value)
30
30
  raise Error, ":expect_threshold_size must be positive" unless bytes.positive?
31
31
 
32
32
  bytes
33
- end
33
+ OUT
34
34
 
35
- def_option(:encodings) do |encs|
36
- raise Error, ":encodings must be a registry" unless encs.respond_to?(:registry)
35
+ def_option(:encodings, <<-OUT)
36
+ raise Error, ":encodings must be a registry" unless value.respond_to?(:registry)
37
37
 
38
- encs
39
- end
38
+ value
39
+ OUT
40
40
  end.new(options).merge(encodings: encodings)
41
41
  end
42
42
  end
@@ -66,7 +66,7 @@ module HTTPX
66
66
 
67
67
  @body = Encoder.new(@body, options.encodings.registry(encoding).deflater)
68
68
  end
69
- @headers["content-length"] = @body.bytesize unless chunked?
69
+ @headers["content-length"] = @body.bytesize unless unbounded_body?
70
70
  end
71
71
  end
72
72
 
@@ -5,12 +5,12 @@ module HTTPX
5
5
  module Compression
6
6
  module Brotli
7
7
  class << self
8
- def load_dependencies(klass)
9
- klass.plugin(:compression)
8
+ def load_dependencies(_klass)
10
9
  require "brotli"
11
10
  end
12
11
 
13
12
  def configure(klass)
13
+ klass.plugin(:compression)
14
14
  klass.default_options.encodings.register "br", self
15
15
  end
16
16
  end
@@ -18,12 +18,13 @@ module HTTPX
18
18
  module Deflater
19
19
  module_function
20
20
 
21
- def deflate(raw, buffer, chunk_size:)
21
+ def deflate(raw, buffer = "".b, chunk_size: 16_384)
22
22
  while (chunk = raw.read(chunk_size))
23
23
  compressed = ::Brotli.deflate(chunk)
24
24
  buffer << compressed
25
25
  yield compressed if block_given?
26
26
  end
27
+ buffer
27
28
  end
28
29
  end
29
30
 
@@ -4,20 +4,20 @@ module HTTPX
4
4
  module Plugins
5
5
  module Compression
6
6
  module Deflate
7
- def self.load_dependencies(klass)
7
+ def self.load_dependencies(_klass)
8
8
  require "stringio"
9
9
  require "zlib"
10
- klass.plugin(:"compression/gzip")
11
10
  end
12
11
 
13
12
  def self.configure(klass)
13
+ klass.plugin(:"compression/gzip")
14
14
  klass.default_options.encodings.register "deflate", self
15
15
  end
16
16
 
17
17
  module Deflater
18
18
  module_function
19
19
 
20
- def deflate(raw, buffer, chunk_size:)
20
+ def deflate(raw, buffer = "".b, chunk_size: 16_384)
21
21
  deflater = Zlib::Deflate.new
22
22
  while (chunk = raw.read(chunk_size))
23
23
  compressed = deflater.deflate(chunk)
@@ -27,6 +27,7 @@ module HTTPX
27
27
  last = deflater.finish
28
28
  buffer << last
29
29
  yield last if block_given?
30
+ buffer
30
31
  ensure
31
32
  deflater.close if deflater
32
33
  end